From be77dc8307d5d832b8d07c91c8b2b5e82f7a1234 Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Sun, 6 Nov 2011 17:18:20 +0000 Subject: [PATCH] Mostly rewrite the visual tools and related classes Convert all coordinates within the visual tools to Vector2D, which has been significantly extended. Eliminates a lot of issues with accumulated rounding errors and simplifies a lot of code. Modernize the visual tools' interactions with the rest of Aegisub by connecting to signals directly rather than routing everything through the video display and converting the main visual tool mode toolbar to the command system. Extract all references to OpenGL from the visual tools and move them to OpenGLWrapper as a first step towards making it possible to implement an alternative video renderer. In the process, eliminate all uses of OpenGL immediate mode. Fix a bunch of minor issues and general instability. Originally committed to SVN as r5823. --- .../aegisub_vs2008/aegisub_vs2008.vcproj | 4 + aegisub/build/msbuild/Aegisub/Aegisub.vcxproj | 1 + .../msbuild/Aegisub/Aegisub.vcxproj.filters | 3 + aegisub/src/agi_pre.h | 3 +- aegisub/src/base_grid.h | 3 +- aegisub/src/command/Makefile | 3 +- aegisub/src/command/command.cpp | 2 + aegisub/src/command/icon.cpp | 7 + aegisub/src/command/video.cpp | 5 +- aegisub/src/command/vis_tool.cpp | 142 ++++ aegisub/src/dialog_properties.cpp | 1 - aegisub/src/dialog_search_replace.cpp | 1 - aegisub/src/dialog_timing_processor.cpp | 1 - aegisub/src/gl_text.cpp | 32 +- aegisub/src/gl_wrap.cpp | 585 +++++++------ aegisub/src/gl_wrap.h | 145 ++-- aegisub/src/include/aegisub/toolbar.h | 2 +- aegisub/src/libresrc/default_hotkey.json | 14 +- aegisub/src/libresrc/default_toolbar.json | 11 + aegisub/src/spline.cpp | 297 +++---- aegisub/src/spline.h | 28 +- aegisub/src/spline_curve.cpp | 221 ++--- aegisub/src/spline_curve.h | 67 +- aegisub/src/subs_edit_box.cpp | 1 - aegisub/src/toolbar.cpp | 10 +- aegisub/src/vector2d.cpp | 280 ++---- aegisub/src/vector2d.h | 123 +-- aegisub/src/video_box.cpp | 14 +- aegisub/src/video_box.h | 11 - aegisub/src/video_context.cpp | 11 +- aegisub/src/video_context.h | 7 - aegisub/src/video_display.cpp | 233 ++--- aegisub/src/video_display.h | 65 +- aegisub/src/visual_feature.cpp | 92 +- aegisub/src/visual_feature.h | 22 +- aegisub/src/visual_tool.cpp | 798 +++++++++--------- aegisub/src/visual_tool.h | 296 ++++--- aegisub/src/visual_tool_clip.cpp | 176 ++-- aegisub/src/visual_tool_clip.h | 59 +- aegisub/src/visual_tool_cross.cpp | 156 ++-- aegisub/src/visual_tool_cross.h | 52 +- aegisub/src/visual_tool_drag.cpp | 363 ++++---- aegisub/src/visual_tool_drag.h | 71 +- aegisub/src/visual_tool_rotatexy.cpp | 274 +++--- aegisub/src/visual_tool_rotatexy.h | 57 +- aegisub/src/visual_tool_rotatez.cpp | 182 ++-- aegisub/src/visual_tool_rotatez.h | 56 +- aegisub/src/visual_tool_scale.cpp | 192 ++--- aegisub/src/visual_tool_scale.h | 50 +- aegisub/src/visual_tool_vector_clip.cpp | 362 +++----- aegisub/src/visual_tool_vector_clip.h | 44 +- 51 files changed, 2440 insertions(+), 3195 deletions(-) create mode 100644 aegisub/src/command/vis_tool.cpp diff --git a/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj b/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj index 9bca1bb19..09215ad58 100644 --- a/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj +++ b/aegisub/build/aegisub_vs2008/aegisub_vs2008.vcproj @@ -1919,6 +1919,10 @@ RelativePath="..\..\src\command\video.cpp" > + + + diff --git a/aegisub/build/msbuild/Aegisub/Aegisub.vcxproj.filters b/aegisub/build/msbuild/Aegisub/Aegisub.vcxproj.filters index ed030b2c6..316a3b72c 100644 --- a/aegisub/build/msbuild/Aegisub/Aegisub.vcxproj.filters +++ b/aegisub/build/msbuild/Aegisub/Aegisub.vcxproj.filters @@ -1196,6 +1196,9 @@ Commands + + Commands + Utilities\UI utilities diff --git a/aegisub/src/agi_pre.h b/aegisub/src/agi_pre.h index 8fd6a7f3d..b7c884ab2 100644 --- a/aegisub/src/agi_pre.h +++ b/aegisub/src/agi_pre.h @@ -160,6 +160,7 @@ #include #include #include +#include #include #include #include @@ -170,11 +171,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include #include diff --git a/aegisub/src/base_grid.h b/aegisub/src/base_grid.h index b90b85d82..58357f8df 100644 --- a/aegisub/src/base_grid.h +++ b/aegisub/src/base_grid.h @@ -121,6 +121,8 @@ protected: void AdjustScrollbar(); void SetColumnWidths(); + bool IsDisplayed(const AssDialogue *line) const; + // Re-implement functions from BaseSelectionController to add batching void AnnounceActiveLineChanged(AssDialogue *new_line); void AnnounceSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed); @@ -139,7 +141,6 @@ public: void EndBatch(); void SetByFrame(bool state); - bool IsDisplayed(const AssDialogue *line) const; void SelectRow(int row, bool addToSelected = false, bool select=true); int GetFirstSelRow() const; wxArrayInt GetSelection() const; diff --git a/aegisub/src/command/Makefile b/aegisub/src/command/Makefile index 6242e4d3c..89c362b1d 100644 --- a/aegisub/src/command/Makefile +++ b/aegisub/src/command/Makefile @@ -21,7 +21,8 @@ SRC = \ time.cpp \ timecode.cpp \ tool.cpp \ - video.cpp + video.cpp \ + vis_tool.cpp SRC += \ icon.cpp \ diff --git a/aegisub/src/command/command.cpp b/aegisub/src/command/command.cpp index e40d719e4..17853fbec 100644 --- a/aegisub/src/command/command.cpp +++ b/aegisub/src/command/command.cpp @@ -88,6 +88,7 @@ namespace cmd { void init_timecode(); void init_tool(); void init_video(); + void init_visual_tools(); void init_builtin_commands() { LOG_D("command/init") << "Populating command map"; @@ -104,6 +105,7 @@ namespace cmd { init_timecode(); init_tool(); init_video(); + init_visual_tools(); } void clear() { diff --git a/aegisub/src/command/icon.cpp b/aegisub/src/command/icon.cpp index f6fb27f58..b1c5a907a 100644 --- a/aegisub/src/command/icon.cpp +++ b/aegisub/src/command/icon.cpp @@ -198,6 +198,13 @@ INSERT_ICON("video/opt/autoscroll", toggle_video_autoscroll) INSERT_ICON("video/play", button_play) INSERT_ICON("video/play/line", button_playline) INSERT_ICON("video/stop", button_pause) +INSERT_ICON("video/tool/clip", visual_clip) +INSERT_ICON("video/tool/cross", visual_standard) +INSERT_ICON("video/tool/drag", visual_move) +INSERT_ICON("video/tool/rotate/xy", visual_rotatexy) +INSERT_ICON("video/tool/rotate/z", visual_rotatez) +INSERT_ICON("video/tool/scale", visual_scale) +INSERT_ICON("video/tool/vector_clip", visual_vector_clip) INSERT_ICON("video/zoom/in", zoom_in_button) INSERT_ICON("video/zoom/out", zoom_out_button) diff --git a/aegisub/src/command/video.cpp b/aegisub/src/command/video.cpp index 2bd7ca4b7..f67a3c1ca 100644 --- a/aegisub/src/command/video.cpp +++ b/aegisub/src/command/video.cpp @@ -238,10 +238,7 @@ struct video_copy_coordinates : public validator_video_loaded { void operator()(agi::Context *c) { if (wxTheClipboard->Open()) { - int x, y; - c->videoBox->videoDisplay->GetMousePosition(&x, &y); - c->videoBox->videoDisplay->ToScriptCoords(&x, &y); - wxTheClipboard->SetData(new wxTextDataObject(wxString::Format("%d,%d", x, y))); + wxTheClipboard->SetData(new wxTextDataObject(c->videoBox->videoDisplay->GetMousePosition().Str())); wxTheClipboard->Close(); } } diff --git a/aegisub/src/command/vis_tool.cpp b/aegisub/src/command/vis_tool.cpp new file mode 100644 index 000000000..a6894895a --- /dev/null +++ b/aegisub/src/command/vis_tool.cpp @@ -0,0 +1,142 @@ +// Copyright (c) 2011, Thomas Goyne +// +// 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/ +// +// $Id$ + +/// @file vis_tool.cpp +/// @brief Visual typesetting tools commands +/// @ingroup command visual_ui +/// + +#include "../config.h" + +#include "command.h" + +#include "../include/aegisub/context.h" +#include "../video_box.h" +#include "../video_context.h" +#include "../video_display.h" +#include "../visual_tool_clip.h" +#include "../visual_tool_cross.h" +#include "../visual_tool_drag.h" +#include "../visual_tool_rotatexy.h" +#include "../visual_tool_rotatez.h" +#include "../visual_tool_scale.h" +#include "../visual_tool_vector_clip.h" + +namespace { + using cmd::Command; + /// @defgroup cmd-visual Visual typesetting tools commands + /// @{ + + struct validator_video_loaded : public Command { + CMD_TYPE(COMMAND_VALIDATE) + bool Validate(const agi::Context *c) { + return c->videoController->IsLoaded(); + } + }; + + struct visual_mode_cross : public validator_video_loaded { + CMD_NAME("video/tool/cross") + STR_MENU("Standard") + STR_DISP("Standard") + STR_HELP("Standard mode, double click sets position.") + + void operator()(agi::Context *c) { + c->videoBox->videoDisplay->SetTool(new VisualToolCross(c->videoBox->videoDisplay, c)); + } + }; + + struct visual_mode_drag : public validator_video_loaded { + CMD_NAME("video/tool/drag") + STR_MENU("Drag") + STR_DISP("Drag") + STR_HELP("Drag subtitles.") + + void operator()(agi::Context *c) { + c->videoBox->videoDisplay->SetTool(new VisualToolDrag(c->videoBox->videoDisplay, c)); + } + }; + + struct visual_mode_rotate_z : public validator_video_loaded { + CMD_NAME("video/tool/rotate/z") + STR_MENU("Rotate Z") + STR_DISP("Rotate Z") + STR_HELP("Rotate subtitles on their Z axis.") + + void operator()(agi::Context *c) { + c->videoBox->videoDisplay->SetTool(new VisualToolRotateZ(c->videoBox->videoDisplay, c)); + } + }; + + struct visual_mode_rotate_xy : public validator_video_loaded { + CMD_NAME("video/tool/rotate/xy") + STR_MENU("Rotate XY") + STR_DISP("Rotate XY") + STR_HELP("Rotate subtitles on their X and Y axes.") + + void operator()(agi::Context *c) { + c->videoBox->videoDisplay->SetTool(new VisualToolRotateXY(c->videoBox->videoDisplay, c)); + } + }; + + struct visual_mode_scale : public validator_video_loaded { + CMD_NAME("video/tool/scale") + STR_MENU("Scale") + STR_DISP("Scale") + STR_HELP("Scale subtitles on X and Y axes.") + + void operator()(agi::Context *c) { + c->videoBox->videoDisplay->SetTool(new VisualToolScale(c->videoBox->videoDisplay, c)); + } + }; + + struct visual_mode_clip : public validator_video_loaded { + CMD_NAME("video/tool/clip") + STR_MENU("Clip") + STR_DISP("Clip") + STR_HELP("Clip subtitles to a rectangle.") + + void operator()(agi::Context *c) { + c->videoBox->videoDisplay->SetTool(new VisualToolClip(c->videoBox->videoDisplay, c)); + } + }; + + struct visual_mode_vector_clip : public validator_video_loaded { + CMD_NAME("video/tool/vector_clip") + STR_MENU("Vector Clip") + STR_DISP("Vector Clip") + STR_HELP("Clip subtitles to a vectorial arean.") + + void operator()(agi::Context *c) { + c->videoBox->videoDisplay->SetTool(new VisualToolVectorClip(c->videoBox->videoDisplay, c)); + } + }; +} + +/// @} + +namespace cmd { + void init_visual_tools() { + reg(new visual_mode_cross); + reg(new visual_mode_drag); + reg(new visual_mode_rotate_z); + reg(new visual_mode_rotate_xy); + reg(new visual_mode_scale); + reg(new visual_mode_clip); + reg(new visual_mode_vector_clip); + } +} diff --git a/aegisub/src/dialog_properties.cpp b/aegisub/src/dialog_properties.cpp index 819361a1e..775ae1b9c 100644 --- a/aegisub/src/dialog_properties.cpp +++ b/aegisub/src/dialog_properties.cpp @@ -52,7 +52,6 @@ #include "utils.h" #include "validators.h" #include "video_context.h" -#include "video_display.h" #include "video_provider_manager.h" DialogProperties::DialogProperties(agi::Context *c) diff --git a/aegisub/src/dialog_search_replace.cpp b/aegisub/src/dialog_search_replace.cpp index 7e2fb63b9..95281e297 100644 --- a/aegisub/src/dialog_search_replace.cpp +++ b/aegisub/src/dialog_search_replace.cpp @@ -52,7 +52,6 @@ #include "selection_controller.h" #include "subs_edit_ctrl.h" #include "subs_grid.h" -#include "video_display.h" // IDs enum { diff --git a/aegisub/src/dialog_timing_processor.cpp b/aegisub/src/dialog_timing_processor.cpp index dd3e4eb4e..a0b19fc06 100644 --- a/aegisub/src/dialog_timing_processor.cpp +++ b/aegisub/src/dialog_timing_processor.cpp @@ -55,7 +55,6 @@ #include "utils.h" #include "validators.h" #include "video_context.h" -#include "video_display.h" /// Window IDs enum { diff --git a/aegisub/src/gl_text.cpp b/aegisub/src/gl_text.cpp index cdb27d6ae..447510089 100644 --- a/aegisub/src/gl_text.cpp +++ b/aegisub/src/gl_text.cpp @@ -342,20 +342,30 @@ void OpenGLTextTexture::Insert(OpenGLTextGlyph &glyph) { /// Draw a glyph at (x,y) void OpenGLTextGlyph::Draw(int x,int y) const { - // Store matrix and translate - glPushMatrix(); - glTranslatef((float)x,(float)y,0.0f); - glBindTexture(GL_TEXTURE_2D, tex); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glBegin(GL_QUADS); - glTexCoord2f(x1,y1); glVertex2f(0,0); // Top-left - glTexCoord2f(x1,y2); glVertex2f(0,h); // Bottom-left - glTexCoord2f(x2,y2); glVertex2f(w,h); // Bottom-right - glTexCoord2f(x2,y1); glVertex2f(w,0); // Top-right - glEnd(); + float tex_coords[] = { + x1, y1, + x1, y2, + x2, y2, + x2, y1 + }; - glPopMatrix(); + float vert_coords[] = { + x, y, + x, y + h, + x + w, y + h, + x + w, y + }; + + glVertexPointer(2, GL_FLOAT, 0, vert_coords); + glTexCoordPointer(2, GL_FLOAT, 0, tex_coords); + glDrawArrays(GL_QUADS, 0, 4); + + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); } /// @brief DOCME diff --git a/aegisub/src/gl_wrap.cpp b/aegisub/src/gl_wrap.cpp index 8772deea4..b1f93180b 100644 --- a/aegisub/src/gl_wrap.cpp +++ b/aegisub/src/gl_wrap.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -42,303 +29,417 @@ #ifndef AGI_PRE #include -#ifdef __APPLE__ +#ifdef HAVE_APPLE_OPENGL_FRAMEWORK +#include +#include #include #else +#include +#include #include "gl/glext.h" #endif - #endif +static const float deg2rad = 3.1415926536f / 180.f; +static const float rad2deg = 180.f / 3.1415926536f; +static const float pi = 3.1415926535897932384626433832795f; + +#ifdef __WIN32__ +#define glGetProc(a) wglGetProcAddress(a) +#elif !defined(__APPLE__) +#include +#define glGetProc(a) glXGetProcAddress((const GLubyte *)(a)) +#endif + +#if defined(__APPLE__) +// Not required on OS X. +#define APIENTRY +#define GL_EXT(type, name) +#else +#define GL_EXT(type, name) \ + static type name = reinterpret_cast(glGetProc(#name)); \ + if (!name) { \ + name = reinterpret_cast(& name ## Fallback); \ + } +#endif + +class VertexArray { + std::vector data; + size_t dim; +public: + VertexArray(size_t dims, size_t elems) { + SetSize(dims, elems); + } + + void SetSize(size_t dims, size_t elems) { + dim = dims; + data.resize(elems * dim); + } + + void Set(size_t i, float x, float y) { + data[i * dim] = x; + data[i * dim + 1] = y; + } + + void Set(size_t i, float x, float y, float z) { + data[i * dim] = x; + data[i * dim + 1] = y; + data[i * dim + 2] = z; + } + + void Set(size_t i, Vector2D p) { + data[i * dim] = p.X(); + data[i * dim + 1] = p.Y(); + } + + void Draw(GLenum mode, bool clear = true) { + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(dim, GL_FLOAT, 0, &data[0]); + glDrawArrays(mode, 0, data.size() / dim); + glDisableClientState(GL_VERTEX_ARRAY); + if (clear) + data.clear(); + } +}; -/// @brief Constructor -/// OpenGLWrapper::OpenGLWrapper() { - r1 = g1 = b1 = a1 = 1.0f; - r2 = g2 = b2 = a2 = 1.0f; - lw = 1; + line_r = line_g = line_b = line_a = 1.f; + fill_r = fill_g = fill_b = fill_a = 1.f; + line_width = 1; + transform_pushed = false; + smooth = true; } - - -/// @brief Draw line -/// @param x1 -/// @param y1 -/// @param x2 -/// @param y2 -/// -void OpenGLWrapper::DrawLine(float x1,float y1,float x2,float y2) const { +void OpenGLWrapper::DrawLine(Vector2D p1, Vector2D p2) const { SetModeLine(); - glBegin(GL_LINES); - glVertex2f(x1,y1); - glVertex2f(x2,y2); - glEnd(); + VertexArray buf(2, 2); + buf.Set(0, p1); + buf.Set(1, p2); + buf.Draw(GL_LINES); } +static inline Vector2D interp(Vector2D p1, Vector2D p2, float t) { + return t * p1 + (1 - t) * p2; +} - -/// @brief Draw line -/// @param x1 -/// @param y1 -/// @param x2 -/// @param y2 -/// @param step -/// -void OpenGLWrapper::DrawDashedLine(float x1,float y1,float x2,float y2,float step) const { - float dist = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); - int steps = (int)((dist-20)/step); - double stepx = double(x2-x1)/steps; - double stepy = double(y2-y1)/steps; - for (int i=0;i r1) { - float temp = r1; - r1 = r2; - r2 = temp; - } +void OpenGLWrapper::DrawRing(Vector2D center, float r1, float r2, float ar, float arc_start, float arc_end) const { + if (r2 > r1) + std::swap(r1, r2); // Arc range - bool hasEnds = arcStart != arcEnd; - float pi = 3.1415926535897932384626433832795f; - arcEnd *= pi / 180.f; - arcStart *= pi / 180.f; - if (arcEnd <= arcStart) arcEnd += 2.0f*pi; - float range = arcEnd - arcStart; + bool needs_end_caps = arc_start != arc_end; + + arc_end *= deg2rad; + arc_start *= deg2rad; + if (arc_end <= arc_start) + arc_end += 2.f * pi; + float range = arc_end - arc_start; // Math - int steps = int((r1 + r1*ar) * range / (2.0f*pi))*4; - if (steps < 12) steps = 12; - //float end = arcEnd; - float step = range/steps; - float curAngle = arcStart; + int steps = std::max(((r1 + r1 * ar) * range / (2.f * pi)) * 4, 12); + float step = range / steps; + float cur_angle = arc_start; - // Fill - if (a2 != 0.0) { + VertexArray buf(2, steps); + + Vector2D scale_inner = Vector2D(ar, 1) * r1; + Vector2D scale_outer = Vector2D(ar, 1) * r2; + + if (fill_a != 0.0) { SetModeFill(); // Annulus if (r1 != r2) { - glBegin(GL_QUADS); - for (int i=0;i const& lines) { + DrawLines(dim, &lines[0], lines.size() / dim); +} -/// DOCME -wxMutex OpenGLWrapper::glMutex; +void OpenGLWrapper::DrawLines(size_t dim, std::vector const& lines, size_t c_dim, std::vector const& colors) { + glShadeModel(GL_SMOOTH); + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(c_dim, GL_FLOAT, 0, &colors[0]); + DrawLines(dim, &lines[0], lines.size() / dim); + glDisableClientState(GL_COLOR_ARRAY); + glShadeModel(GL_FLAT); +} +void OpenGLWrapper::DrawLines(size_t dim, const float *lines, size_t n) { + SetModeLine(); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(dim, GL_FLOAT, 0, lines); + glDrawArrays(GL_LINES, 0, n); + glDisableClientState(GL_VERTEX_ARRAY); +} +void OpenGLWrapper::DrawLineStrip(size_t dim, std::vector const& lines) { + SetModeLine(); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(dim, GL_FLOAT, 0, &lines[0]); + glDrawArrays(GL_LINE_STRIP, 0, lines.size() / dim); + glDisableClientState(GL_VERTEX_ARRAY); +} + +// Substitute for glMultiDrawArrays for sub-1.4 OpenGL +// Not required on OS X. +#ifndef __APPLE__ +static void APIENTRY glMultiDrawArraysFallback(GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) { + for (int i = 0; i < primcount; ++i) { + glDrawArrays(mode, *first++, *count++); + } +} +#endif + +void OpenGLWrapper::DrawMultiPolygon(std::vector const& points, std::vector &start, std::vector &count, Vector2D video_size, bool invert) { + GL_EXT(PFNGLMULTIDRAWARRAYSPROC, glMultiDrawArrays); + + // The following is nonzero winding-number PIP based on stencils + + // Draw to stencil only + glEnable(GL_STENCIL_TEST); + glColorMask(0, 0, 0, 0); + + // GL_INCR_WRAP was added in 1.4, so instead set the entire stencil to 128 + // and wobble from there + glStencilFunc(GL_NEVER, 128, 0xFF); + glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); + + VertexArray buf(2, 4); + buf.Set(0, Vector2D()); + buf.Set(1, Vector2D(video_size, 0)); + buf.Set(2, video_size); + buf.Set(3, Vector2D(0, video_size)); + glColor4f(0, 0, 0, 1); + glDisable(GL_BLEND); + buf.Draw(GL_QUADS, false); + + // Increment the winding number for each forward facing triangle + glStencilOp(GL_INCR, GL_INCR, GL_INCR); + glEnable(GL_CULL_FACE); + + glCullFace(GL_BACK); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, &points[0]); + glMultiDrawArrays(GL_TRIANGLE_FAN, &start[0], &count[0], start.size()); + + // Decrement the winding number for each backfacing triangle + glStencilOp(GL_DECR, GL_DECR, GL_DECR); + glCullFace(GL_FRONT); + glMultiDrawArrays(GL_TRIANGLE_FAN, &start[0], &count[0], start.size()); + glDisable(GL_CULL_FACE); + + // Draw the actual rectangle + glColorMask(1, 1, 1, 1); + float real_line_a = line_a; + line_a = 0; + + // VSFilter draws when the winding number is nonzero, so we want to draw the + // mask when the winding number is zero (where 128 is zero due to the lack of + // wrapping combined with unsigned numbers) + glStencilFunc(invert ? GL_EQUAL : GL_NOTEQUAL, 128, 0xFF); + DrawRectangle(Vector2D(), video_size); + glDisable(GL_STENCIL_TEST); + + // Draw lines + line_a = real_line_a; + SetModeLine(); + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, &points[0]); + glMultiDrawArrays(GL_LINE_LOOP, &start[0], &count[0], start.size()); + + glDisableClientState(GL_VERTEX_ARRAY); +} + +void OpenGLWrapper::SetOrigin(Vector2D origin) { + PrepareTransform(); + glTranslatef(origin.X(), origin.Y(), -1.f); +} + +void OpenGLWrapper::SetScale(Vector2D scale) { + PrepareTransform(); + glScalef(scale.X() / 100.f, scale.Y() / 100.f, 1.f); +} + +void OpenGLWrapper::SetRotation(float x, float y, float z) { + PrepareTransform(); + float matrix[16] = { 2500, 0, 0, 0, 0, 2500, 0, 0, 0, 0, 1, 1, 0, 0, 2500, 2500 }; + glMultMatrixf(matrix); + glScalef(1.f, 1.f, 8.f); + glRotatef(y, 0.f, -1.f, 0.f); + glRotatef(x, -1.f, 0.f, 0.f); + glRotatef(z, 0.f, 0.f, -1.f); +} + +void OpenGLWrapper::PrepareTransform() { + if (!transform_pushed) { + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + transform_pushed = true; + } +} + +void OpenGLWrapper::ResetTransform() { + if (transform_pushed) { + glPopMatrix(); + transform_pushed = false; + } +} diff --git a/aegisub/src/gl_wrap.h b/aegisub/src/gl_wrap.h index ef82cc7ea..87470b52c 100644 --- a/aegisub/src/gl_wrap.h +++ b/aegisub/src/gl_wrap.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -34,41 +21,13 @@ /// @ingroup video_output /// - -#ifdef __APPLE__ -#include -#include -#else -#include -#include - -/// DOCME -typedef GLuint GLhandleARB; -#endif +#include "vector2d.h" #ifndef AGI_PRE -#include -#include -#endif - -#ifdef __WIN32__ -#define glGetProc(a) wglGetProcAddress(a) -#else -#define glGetProc(a) glXGetProcAddress((const GLubyte *)(a)) -#endif - -#if defined(__APPLE__) -// Not required on OS X. -#define GL_EXT(type, name) -#else - -#define GL_EXT(type, name) \ - static type name = reinterpret_cast(glGetProc(#name)); \ - if (!name) { \ - name = reinterpret_cast(& name ## Fallback); \ - } +#include #endif +class wxColour; /// DOCME /// @class OpenGLWrapper @@ -76,55 +35,51 @@ typedef GLuint GLhandleARB; /// /// DOCME class OpenGLWrapper { -private: + float line_r, line_g, line_b, line_a; + float fill_r, fill_g, fill_b, fill_a; - /// DOCME + int line_width; + bool smooth; - /// DOCME - - /// DOCME - - /// DOCME - float r1,g1,b1,a1; - - /// DOCME - - /// DOCME - - /// DOCME - - /// DOCME - float r2,g2,b2,a2; - - /// DOCME - int lw; + bool transform_pushed; + void PrepareTransform(); public: OpenGLWrapper(); - - /// DOCME - static wxMutex glMutex; - - void SetLineColour(wxColour col,float alpha=1.0f,int width=1); - void SetFillColour(wxColour col,float alpha=1.0f); + void SetLineColour(wxColour col, float alpha = 1.0f, int width = 1); + void SetFillColour(wxColour col, float alpha = 1.0f); void SetModeLine() const; void SetModeFill() const; - void DrawLine(float x1,float y1,float x2,float y2) const; - void DrawDashedLine(float x1,float y1,float x2,float y2,float dashLen) const; - void DrawEllipse(float x,float y,float radiusX,float radiusY) const; - /// @brief DOCME - /// @param x - /// @param y - /// @param radius - /// - void DrawCircle(float x,float y,float radius) const { DrawEllipse(x,y,radius,radius); } - void DrawRectangle(float x1,float y1,float x2,float y2) const; - void DrawRing(float x,float y,float r1,float r2,float ar=1.0f,float arcStart=0.0f,float arcEnd=0.0f) const; - void DrawTriangle(float x1,float y1,float x2,float y2,float x3,float y3) const; + void SetInvert(); + void ClearInvert(); + + void SetScale(Vector2D scale); + void SetOrigin(Vector2D origin); + void SetRotation(float x, float y, float z); + void ResetTransform(); + + void DrawLine(Vector2D p1, Vector2D p2) const; + void DrawDashedLine(Vector2D p1, Vector2D p2, float dashLen) const; + void DrawEllipse(Vector2D center, Vector2D radius) const; + void DrawCircle(Vector2D center, float radius) const { DrawEllipse(center, Vector2D(radius, radius)); } + void DrawRectangle(Vector2D p1, Vector2D p2) const; + void DrawRing(Vector2D center, float r1, float r2, float ar = 1.0f, float arcStart = 0.0f, float arcEnd = 0.0f) const; + void DrawTriangle(Vector2D p1, Vector2D p2, Vector2D p3) const; + + void DrawLines(size_t dim, std::vector const& lines); + void DrawLines(size_t dim, std::vector const& lines, size_t c_dim, std::vector const& colors); + void DrawLines(size_t dim, const float *lines, size_t n); + void DrawLineStrip(size_t dim, std::vector const& lines); + + /// Draw a multipolygon serialized into a single array + /// @param points List of coordinates + /// @param start Indices in points which are the start of a new polygon + /// @param count Number of points in each polygon + /// @param video_size Bottom-right corner of the visible area + /// @param invert Draw the area outside the polygons instead + void DrawMultiPolygon(std::vector const& points, std::vector &start, std::vector &count, Vector2D video_size, bool invert); static bool IsExtensionSupported(const char *ext); }; - - diff --git a/aegisub/src/include/aegisub/toolbar.h b/aegisub/src/include/aegisub/toolbar.h index 6c4d5d890..c1259b36f 100644 --- a/aegisub/src/include/aegisub/toolbar.h +++ b/aegisub/src/include/aegisub/toolbar.h @@ -34,5 +34,5 @@ namespace toolbar { /// @param context Project context /// @param hotkey Hotkey context for the tooltip void AttachToolbar(wxFrame *frame, std::string const& name, agi::Context *context, std::string const& hotkey); - wxToolBar *GetToolbar(wxWindow *parent, std::string const& name, agi::Context *context, std::string const& hotkey); + wxToolBar *GetToolbar(wxWindow *parent, std::string const& name, agi::Context *context, std::string const& hotkey, bool vertical = false); } diff --git a/aegisub/src/libresrc/default_hotkey.json b/aegisub/src/libresrc/default_hotkey.json index ae2373834..839eea4c0 100644 --- a/aegisub/src/libresrc/default_hotkey.json +++ b/aegisub/src/libresrc/default_hotkey.json @@ -474,43 +474,43 @@ "key" : "Left" } ], - "visual typesetting set tool crosshair" : [ + "video/tool/cross" : [ { "modifiers" : [], "key" : "A" } ], - "visual typesetting set tool drag" : [ + "video/tool/drag" : [ { "modifiers" : [], "key" : "S" } ], - "visual typesetting set tool rectangle clip" : [ + "video/tool/clip" : [ { "modifiers" : [], "key" : "H" } ], - "visual typesetting set tool rotate xy" : [ + "video/tool/rotate/xy" : [ { "modifiers" : [], "key" : "F" } ], - "visual typesetting set tool rotate z" : [ + "video/tool/rotate/z" : [ { "modifiers" : [], "key" : "D" } ], - "visual typesetting set tool scale" : [ + "video/tool/scale" : [ { "modifiers" : [], "key" : "G" } ], - "visual typesetting set tool vector clip" : [ + "video/tool/vector_clip" : [ { "modifiers" : [], "key" : "J" diff --git a/aegisub/src/libresrc/default_toolbar.json b/aegisub/src/libresrc/default_toolbar.json index 0e85cf05b..fb67293ed 100644 --- a/aegisub/src/libresrc/default_toolbar.json +++ b/aegisub/src/libresrc/default_toolbar.json @@ -61,5 +61,16 @@ "", "app/options", "grid/tag/cycle_hiding" + ], + "visual_tools" : [ + "video/tool/cross", + "video/tool/drag", + "video/tool/rotate/z", + "video/tool/rotate/xy", + "video/tool/scale", + "video/tool/clip", + "video/tool/vector_clip", + "", + "help/video" ] } diff --git a/aegisub/src/spline.cpp b/aegisub/src/spline.cpp index f73156350..f4f9812d6 100644 --- a/aegisub/src/spline.cpp +++ b/aegisub/src/spline.cpp @@ -37,192 +37,164 @@ #include "config.h" #ifndef AGI_PRE +#include + #include #endif -#include - #include "spline.h" -#include "utils.h" -#include "video_display.h" -/// @brief Spline constructor -Spline::Spline(const VideoDisplay &scale) : scale(scale) { +#include "utils.h" +#include "visual_tool.h" + +Spline::Spline(const VisualToolBase &scale) : scale(scale) { } /// @brief Encode to ASS wxString Spline::EncodeToASS() { wxString result; - char lastCommand = 0; + result.reserve(size() * 10); + char last = 0; - // Insert each element - for (iterator cur=begin();cur!=end();cur++) { - // Each curve + for (iterator cur = begin(); cur != end(); ++cur) { switch (cur->type) { - case CURVE_POINT: { - if (lastCommand != 'm') { + case SplineCurve::POINT: + if (last != 'm') { result += "m "; - lastCommand = 'm'; + last = 'm'; } - int x = cur->p1.x; - int y = cur->p1.y; - scale.ToScriptCoords(&x, &y); - result += wxString::Format("%i %i ", x, y); + result += scale.ToScriptCoords(cur->p1).DStr(' '); break; - } - case CURVE_LINE: { - if (lastCommand != 'l') { + + case SplineCurve::LINE: + if (last != 'l') { result += "l "; - lastCommand = 'l'; + last = 'l'; } - int x = cur->p2.x; - int y = cur->p2.y; - scale.ToScriptCoords(&x, &y); - result += wxString::Format("%i %i ", x, y); + result += scale.ToScriptCoords(cur->p2).DStr(' '); break; - } - case CURVE_BICUBIC: { - if (lastCommand != 'b') { + + case SplineCurve::BICUBIC: + if (last != 'b') { result += "b "; - lastCommand = 'b'; + last = 'b'; } - int x2 = cur->p2.x; - int y2 = cur->p2.y; - int x3 = cur->p3.x; - int y3 = cur->p3.y; - int x4 = cur->p4.x; - int y4 = cur->p4.y; - scale.ToScriptCoords(&x2, &y2); - scale.ToScriptCoords(&x3, &y3); - scale.ToScriptCoords(&x4, &y4); - result += wxString::Format("%i %i %i %i %i %i ", x2, y2, x3, y3, x4, y4); + result += scale.ToScriptCoords(cur->p2).DStr(' '); + result += scale.ToScriptCoords(cur->p3).DStr(' '); + result += scale.ToScriptCoords(cur->p4).DStr(' '); break; - } + default: break; } + result += " "; } return result; } -/// @brief Decode from ASS -/// @param str void Spline::DecodeFromASS(wxString str) { // Clear current clear(); - std::vector stack; + std::vector stack; // Prepare - char lastCommand = 'm'; - int x = 0; - int y = 0; + char command = 'm'; + Vector2D pt; // Tokenize the string - wxStringTokenizer tkn(str," "); + wxStringTokenizer tkn(str, " "); while (tkn.HasMoreTokens()) { wxString token = tkn.GetNextToken(); - - // Got a number - if (token.IsNumber()) { - long n; - token.ToLong(&n); + double n; + if (token.ToCDouble(&n)) { stack.push_back(n); // Move - if (stack.size() == 2 && lastCommand == 'm') { - scale.FromScriptCoords(&stack[0], &stack[1]); - SplineCurve curve; - x = curve.p1.x = stack[0]; - y = curve.p1.y = stack[1]; - curve.type = CURVE_POINT; + if (stack.size() == 2 && command == 'm') { + pt = scale.FromScriptCoords(Vector2D(stack[0], stack[1])); stack.clear(); - push_back(curve); + + push_back(pt); } // Line - if (stack.size() == 2 && lastCommand == 'l') { - scale.FromScriptCoords(&stack[0], &stack[1]); - SplineCurve curve; - curve.p1.x = x; - curve.p1.y = y; - x = curve.p2.x = stack[0]; - y = curve.p2.y = stack[1]; - curve.type = CURVE_LINE; - stack.clear(); + if (stack.size() == 2 && command == 'l') { + SplineCurve curve(pt, scale.FromScriptCoords(Vector2D(stack[0], stack[1]))); push_back(curve); + + pt = curve.p2; + stack.clear(); } // Bicubic - else if (stack.size() == 6 && lastCommand == 'b') { - scale.FromScriptCoords(&stack[0], &stack[1]); - scale.FromScriptCoords(&stack[2], &stack[3]); - scale.FromScriptCoords(&stack[4], &stack[5]); - SplineCurve curve; - curve.p1.x = x; - curve.p1.y = y; - curve.p2.x = stack[0]; - curve.p2.y = stack[1]; - curve.p3.x = stack[2]; - curve.p3.y = stack[3]; - curve.p4.x = stack[4]; - curve.p4.y = stack[5]; - curve.type = CURVE_BICUBIC; - x = curve.p4.x; - y = curve.p4.y; - stack.clear(); + else if (stack.size() == 6 && command == 'b') { + SplineCurve curve(pt, + scale.FromScriptCoords(Vector2D(stack[0], stack[1])), + scale.FromScriptCoords(Vector2D(stack[2], stack[3])), + scale.FromScriptCoords(Vector2D(stack[4], stack[5]))); push_back(curve); - } - // Close - else if (lastCommand == 'c') { + pt = curve.p4; stack.clear(); } } // Got something else - else { - if (token == "m") lastCommand = 'm'; - else if (token == "l") lastCommand = 'l'; - else if (token == "b") lastCommand = 'b'; - else if (token == "n") lastCommand = 'n'; - else if (token == "s") lastCommand = 's'; - else if (token == "c") lastCommand = 'c'; + else if (token.size() == 1) { + command = token[0]; + stack.clear(); } } } -/// @brief Moves a specific point in the spline -/// @param curveIndex -/// @param point -/// @param pos -void Spline::MovePoint(iterator curve,int point,Vector2D const& pos) { +void Spline::MovePoint(iterator curve,int point,Vector2D pos) { iterator prev = curve; if (curve != begin()) --prev; iterator next = curve; ++next; - if (next != end() && next->type == CURVE_POINT) next = end(); + if (next != end() && next->type == SplineCurve::POINT) + next = end(); // Modify if (point == 0) { curve->p1 = pos; - if (curve != begin() && curve->type != CURVE_POINT) prev->EndPoint() = pos; - if (next != end() && curve->type == CURVE_POINT) next->p1 = pos; + if (curve != begin() && curve->type != SplineCurve::POINT) + prev->EndPoint() = pos; + if (next != end() && curve->type == SplineCurve::POINT) + next->p1 = pos; } else if (point == 1) { curve->p2 = pos; - if (next != end() && curve->type == CURVE_LINE) next->p1 = pos; + if (next != end() && curve->type == SplineCurve::LINE) + next->p1 = pos; } else if (point == 2) { curve->p3 = pos; } else if (point == 3) { curve->p4 = pos; - if (next != end()) next->p1 = pos; + if (next != end()) + next->p1 = pos; } } -/// @brief Gets a list of points in the curve -/// @param points -/// @param pointCurve +static int render_bicubic(Spline::iterator cur, std::vector &points) { + int len = int( + (cur->p2 - cur->p1).Len() + + (cur->p3 - cur->p2).Len() + + (cur->p4 - cur->p3).Len()); + int steps = len/8; + + for (int i = 0; i <= steps; ++i) { + // Get t and t-1 (u) + float t = i / float(steps); + Vector2D p = cur->GetPoint(t); + points.push_back(p.X()); + points.push_back(p.Y()); + } + + return steps; +} + void Spline::GetPointList(std::vector& points, std::vector& first, std::vector& count) { points.clear(); first.clear(); @@ -232,106 +204,65 @@ void Spline::GetPointList(std::vector& points, std::vector& first, s int curCount = 0; // Generate points for each curve - for (iterator cur = begin();cur!=end();cur++) { + for (iterator cur = begin(); cur != end(); ++cur) { switch (cur->type) { - case CURVE_POINT: - if (curCount > 0) { + case SplineCurve::POINT: + if (curCount > 0) count.push_back(curCount); - } // start new path first.push_back(points.size() / 2); - points.push_back(cur->p1.x); - points.push_back(cur->p1.y); + points.push_back(cur->p1.X()); + points.push_back(cur->p1.Y()); curCount = 1; break; - case CURVE_LINE: - points.push_back(cur->p2.x); - points.push_back(cur->p2.y); - curCount++; - break; - case CURVE_BICUBIC: { - // Get the control points - Vector2D p1 = cur->p1; - Vector2D p2 = cur->p2; - Vector2D p3 = cur->p3; - Vector2D p4 = cur->p4; - // Find number of steps - int len = (int)((p2-p1).Len() + (p3-p2).Len() + (p4-p3).Len()); - int steps = len/8; - - // Render curve - for (int i=1;i<=steps;i++) { - // Get t and t-1 (u) - float t = float(i)/float(steps); - Vector2D p = cur->GetPoint(t); - points.push_back(p.x); - points.push_back(p.y); - } - curCount += steps; + case SplineCurve::LINE: + points.push_back(cur->p2.X()); + points.push_back(cur->p2.Y()); + ++curCount; break; - } + + case SplineCurve::BICUBIC: + curCount += render_bicubic(cur, points); + break; + default: break; } } count.push_back(curCount); } + void Spline::GetPointList(std::vector &points, iterator curve) { points.clear(); if (curve == end()) return; switch (curve->type) { - case CURVE_LINE: - points.push_back(curve->p1.x); - points.push_back(curve->p1.y); - points.push_back(curve->p2.x); - points.push_back(curve->p2.y); + case SplineCurve::LINE: + points.push_back(curve->p1.X()); + points.push_back(curve->p1.Y()); + points.push_back(curve->p2.X()); + points.push_back(curve->p2.Y()); break; - case CURVE_BICUBIC: { - // Get the control points - Vector2D p1 = curve->p1; - Vector2D p2 = curve->p2; - Vector2D p3 = curve->p3; - Vector2D p4 = curve->p4; - // Find number of steps - int len = (int)((p2-p1).Len() + (p3-p2).Len() + (p4-p3).Len()); - int steps = len/8; - - // Render curve - for (int i=0;i<=steps;i++) { - // Get t and t-1 (u) - float t = float(i)/float(steps); - Vector2D p = curve->GetPoint(t); - points.push_back(p.x); - points.push_back(p.y); - } + case SplineCurve::BICUBIC: + render_bicubic(curve, points); break; - } + default: break; } } -/// @brief t value and curve of the point closest to reference -/// @param reference -/// @param curve -/// @param t -/// @param pt -void Spline::GetClosestParametricPoint(Vector2D const& reference,iterator &curve,float &t,Vector2D &pt) { +void Spline::GetClosestParametricPoint(Vector2D reference,iterator &curve,float &t,Vector2D &pt) { curve = end(); t = 0.f; if (empty()) return; // Close the shape - SplineCurve pad; - pad.p1 = back().EndPoint(); - pad.p2 = front().p1; - pad.type = CURVE_LINE; - push_back(pad); + push_back(SplineCurve(back().EndPoint(), front().p1)); float closest = std::numeric_limits::infinity(); - for (iterator cur = begin();cur!=end();cur++) { + for (iterator cur = begin(); cur != end(); ++cur) { float param = cur->GetClosestParam(reference); Vector2D p1 = cur->GetPoint(param); float dist = (p1-reference).SquareLen(); @@ -351,19 +282,14 @@ void Spline::GetClosestParametricPoint(Vector2D const& reference,iterator &curve pop_back(); } -/// @brief Point closest to reference -/// @param reference -/// @return -Vector2D Spline::GetClosestPoint(Vector2D const& reference) { +Vector2D Spline::GetClosestPoint(Vector2D reference) { iterator curve; float t; Vector2D point; - GetClosestParametricPoint(reference,curve,t,point); + GetClosestParametricPoint(reference, curve, t, point); return point; } -/// @brief Smoothes the spline -/// @param smooth void Spline::Smooth(float smooth) { // See if there are enough curves if (size() < 3) return; @@ -371,13 +297,12 @@ void Spline::Smooth(float smooth) { // Smooth curve iterator curve1 = end(); --curve1; - for (iterator cur = begin(); cur != end();) { + for (iterator cur = begin(); cur != end(); ) { iterator curve0 = curve1; curve1 = cur; - cur++; + ++cur; iterator curve2 = cur == end() ? begin() : cur; - // Smooth curve - curve1->Smooth(curve0->p1,curve2->p2,smooth); + curve1->Smooth(curve0->p1, curve2->p2, smooth); } } diff --git a/aegisub/src/spline.h b/aegisub/src/spline.h index 98e9f90c8..0eabd68f1 100644 --- a/aegisub/src/spline.h +++ b/aegisub/src/spline.h @@ -43,29 +43,41 @@ #include "spline_curve.h" -class VideoDisplay; +class VisualToolBase; /// DOCME /// @class Spline /// @brief DOCME class Spline : private std::list { -private: - const VideoDisplay &scale; + const VisualToolBase &scale; public: - Spline(const VideoDisplay &scale); + Spline(const VisualToolBase &scale); + /// Encode to an ASS vector drawing wxString EncodeToASS(); + + /// Decode an ASS vector drawing void DecodeFromASS(wxString str); - void MovePoint(iterator curve,int point,Vector2D const& pos); + /// @brief Moves a specific point in the spline + /// @param curve Curve which the point is in + /// @param point Index in the curve + /// @param pos New position + void MovePoint(iterator curve, int point, Vector2D pos); + + /// Smooth the spline void Smooth(float smooth=1.0f); + /// Gets a list of points in the curve void GetPointList(std::vector& points, std::vector& first, std::vector& count); + /// Gets a list of points in the curve void GetPointList(std::vector &points, iterator curve); - void GetClosestParametricPoint(Vector2D const& reference, iterator& curve, float &t, Vector2D &point); - Vector2D GetClosestPoint(Vector2D const& reference); - Vector2D GetClosestControlPoint(Vector2D const& reference); + /// Get t value and curve of the point closest to reference + void GetClosestParametricPoint(Vector2D reference, iterator& curve, float &t, Vector2D &point); + /// Get closest point on the curve to reference + Vector2D GetClosestPoint(Vector2D reference); + Vector2D GetClosestControlPoint(Vector2D reference); // This list intentionally excludes things specific to std::list using std::list::value_type; diff --git a/aegisub/src/spline_curve.cpp b/aegisub/src/spline_curve.cpp index 839e35d31..732ec1451 100644 --- a/aegisub/src/spline_curve.cpp +++ b/aegisub/src/spline_curve.cpp @@ -34,145 +34,103 @@ /// @ingroup visual_ts /// -/////////// -// Headers #include "config.h" #include "spline_curve.h" #include "utils.h" -/// @brief Curve constructor -/// -SplineCurve::SplineCurve() { - type = CURVE_INVALID; +#ifndef AGI_PRE +#include +#include +#endif + +SplineCurve::SplineCurve(Vector2D p1) : p1(p1), type(POINT) { } +SplineCurve::SplineCurve(Vector2D p1, Vector2D p2) : p1(p1), p2(p2), type(LINE) { } +SplineCurve::SplineCurve(Vector2D p1, Vector2D p2, Vector2D p3, Vector2D p4) +: p1(p1), p2(p2), p3(p3), p4(p4), type(BICUBIC) +{ } -/// @brief Split a curve in two using the de Casteljau algorithm -/// @param c1 -/// @param c2 -/// @param t -/// -void SplineCurve::Split(SplineCurve &c1,SplineCurve &c2,float t) { - // Split a line - if (type == CURVE_LINE) { - c1.type = CURVE_LINE; - c2.type = CURVE_LINE; - c1.p1 = p1; - c2.p2 = p2; - c1.p2 = p1*(1-t)+p2*t; - c2.p1 = c1.p2; +void SplineCurve::Split(SplineCurve &c1, SplineCurve &c2, float t) { + if (type == LINE) { + c1 = SplineCurve(p1, p1 * (1 - t) + p2 * t); + c2 = SplineCurve(c1.p2, p2); } + else if (type == BICUBIC) { + float u = 1 - t; + Vector2D p12 = p1 * u + p2 * t; + Vector2D p23 = p2 * u + p3 * t; + Vector2D p34 = p3 * u + p4 * t; + Vector2D p123 = p12 * u + p23 * t; + Vector2D p234 = p23 * u + p34 * t; + Vector2D p1234 = p123 * u + p234 * t; - // Split a bicubic - else if (type == CURVE_BICUBIC) { - c1.type = CURVE_BICUBIC; - c2.type = CURVE_BICUBIC; - - // Sub-divisions - float u = 1-t; - Vector2D p12 = p1*u+p2*t; - Vector2D p23 = p2*u+p3*t; - Vector2D p34 = p3*u+p4*t; - Vector2D p123 = p12*u+p23*t; - Vector2D p234 = p23*u+p34*t; - Vector2D p1234 = p123*u+p234*t; - - // Set points - c1.p1 = p1; - c2.p4 = p4; - c1.p2 = p12; - c1.p3 = p123; - c1.p4 = p1234; - c2.p1 = p1234; - c2.p2 = p234; - c2.p3 = p34; + c1 = SplineCurve(p1, p12, p123, p1234); + c2 = SplineCurve(p1234, p234, p34, p4); } } -/// @brief Based on http://antigrain.com/research/bezier_interpolation/index.html Smoothes the curve -/// @param P0 -/// @param P3 -/// @param smooth -/// @return -/// -void SplineCurve::Smooth(Vector2D const& P0,Vector2D const& P3,float smooth) { - // Validate - if (type != CURVE_LINE) return; - if (p1 == p2) return; - smooth = mid(0.f,smooth,1.f); - - // Get points - Vector2D P1 = p1; - Vector2D P2 = p2; +void SplineCurve::Smooth(Vector2D p0, Vector2D p3, float smooth) { + if (type != LINE || p1 == p2) return; + smooth = mid(0.f, smooth, 1.f); // Calculate intermediate points - Vector2D c1 = (P0+P1)/2.f; - Vector2D c2 = (P1+P2)/2.f; - Vector2D c3 = (P2+P3)/2.f; - float len1 = (P1-P0).Len(); - float len2 = (P2-P1).Len(); - float len3 = (P3-P2).Len(); - float k1 = len1/(len1+len2); - float k2 = len2/(len2+len3); - Vector2D m1 = c1+(c2-c1)*k1; - Vector2D m2 = c2+(c3-c2)*k2; + Vector2D c1 = (p0 + p1) / 2.f; + Vector2D c2 = (p1 + p2) / 2.f; + Vector2D c3 = (p2 + p3) / 2.f; + + float len1 = (p1 - p0).Len(); + float len2 = (p2 - p1).Len(); + float len3 = (p3 - p2).Len(); + + float k1 = len1 / (len1 + len2); + float k2 = len2 / (len2 + len3); + + Vector2D m1 = c1 + (c2 - c1) * k1; + Vector2D m2 = c2 + (c3 - c2) * k2; // Set curve points p4 = p2; - p2 = m1+(c2-m1)*smooth + P1 - m1; - p3 = m2+(c2-m2)*smooth + P2 - m2; - type = CURVE_BICUBIC; + p3 = m2 + (c2 - m2) * smooth + p2 - m2; + p2 = m1 + (c2 - m1) * smooth + p1 - m1; + type = BICUBIC; } -/// @brief Get a point -/// @param t -/// @return -/// Vector2D SplineCurve::GetPoint(float t) const { - if (type == CURVE_POINT) return p1; - if (type == CURVE_LINE) { - return p1*(1.f-t) + p2*t; - } - if (type == CURVE_BICUBIC) { - float u = 1.f-t; - return p1*u*u*u + 3*p2*t*u*u + 3*p3*t*t*u + p4*t*t*t; - } + float u = 1.f - t; - return Vector2D(0,0); + if (type == POINT) + return p1; + if (type == LINE) + return p1 * u + p2 * t; + + return p1*u*u*u + 3*p2*t*u*u + 3*p3*t*t*u + p4*t*t*t; } Vector2D& SplineCurve::EndPoint() { switch (type) { - case CURVE_POINT: return p1; - case CURVE_LINE: return p2; - case CURVE_BICUBIC: return p4; - default: return p1; + case POINT: return p1; + case LINE: return p2; + case BICUBIC: return p4; + default: return p1; } } -/// @brief Get point closest to reference -/// @param ref -/// @return -/// -Vector2D SplineCurve::GetClosestPoint(Vector2D const& ref) const { +Vector2D SplineCurve::GetClosestPoint(Vector2D ref) const { return GetPoint(GetClosestParam(ref)); } -/// @brief Get value of parameter closest to point -/// @param ref -/// @return -/// -float SplineCurve::GetClosestParam(Vector2D const& ref) const { - if (type == CURVE_LINE) { - return GetClosestSegmentPart(p1,p2,ref); - } - if (type == CURVE_BICUBIC) { +float SplineCurve::GetClosestParam(Vector2D ref) const { + if (type == LINE) + return GetClosestSegmentPart(p1, p2, ref); + + if (type == BICUBIC) { int steps = 100; - float bestDist = 80000000.f; + float bestDist = std::numeric_limits::max(); float bestT = 0.f; - for (int i=0;i<=steps;i++) { - float t = float(i)/float(steps); - float dist = (GetPoint(t)-ref).Len(); + for (int i = 0; i <= steps; ++i) { + float t = i / float(steps); + float dist = (GetPoint(t) - ref).SquareLen(); if (dist < bestDist) { bestDist = dist; bestT = t; @@ -180,45 +138,30 @@ float SplineCurve::GetClosestParam(Vector2D const& ref) const { } return bestT; } + return 0.f; } -/// @brief Quick distance -/// @param ref -/// @return -/// -float SplineCurve::GetQuickDistance(Vector2D const& ref) const { - using std::min; - if (type == CURVE_BICUBIC) { - float len1 = GetClosestSegmentDistance(p1,p2,ref); - float len2 = GetClosestSegmentDistance(p2,p3,ref); - float len3 = GetClosestSegmentDistance(p3,p4,ref); - float len4 = GetClosestSegmentDistance(p4,p1,ref); - float len5 = GetClosestSegmentDistance(p1,p3,ref); - float len6 = GetClosestSegmentDistance(p2,p4,ref); - return min(min(min(len1,len2),min(len3,len4)),min(len5,len6)); +float SplineCurve::GetQuickDistance(Vector2D ref) const { + if (type == BICUBIC) { + float lens[] = { + GetClosestSegmentDistance(p1, p2, ref), + GetClosestSegmentDistance(p2, p3, ref), + GetClosestSegmentDistance(p3, p4, ref), + GetClosestSegmentDistance(p4, p1, ref), + GetClosestSegmentDistance(p1, p3, ref), + GetClosestSegmentDistance(p2, p4, ref) + }; + return *std::min_element(lens, lens + 6); } - - // Something else - else return (GetClosestPoint(ref)-ref).Len(); + return (GetClosestPoint(ref) - ref).Len(); } -/// @brief Closest t in segment p1-p2 to point p3 -/// @param pt1 -/// @param pt2 -/// @param pt3 -/// @return -/// -float SplineCurve::GetClosestSegmentPart(Vector2D const& pt1,Vector2D const& pt2,Vector2D const& pt3) const { - return mid(0.f,(pt3-pt1).Dot(pt2-pt1)/(pt2-pt1).SquareLen(),1.f); +float SplineCurve::GetClosestSegmentPart(Vector2D pt1, Vector2D pt2, Vector2D pt3) const { + return mid(0.f, (pt3 - pt1).Dot(pt2 - pt1) / (pt2 - pt1).SquareLen(), 1.f); } -/// @brief Closest distance between p3 and segment p1-p2 -/// @param pt1 -/// @param pt2 -/// @param pt3 -/// -float SplineCurve::GetClosestSegmentDistance(Vector2D const& pt1,Vector2D const& pt2,Vector2D const& pt3) const { - float t = GetClosestSegmentPart(pt1,pt2,pt3); - return (pt1*(1.f-t)+pt2*t-pt3).Len(); +float SplineCurve::GetClosestSegmentDistance(Vector2D pt1, Vector2D pt2, Vector2D pt3) const { + float t = GetClosestSegmentPart(pt1, pt2, pt3); + return (pt1 * (1.f - t) + pt2 * t - pt3).Len(); } diff --git a/aegisub/src/spline_curve.h b/aegisub/src/spline_curve.h index 0da098ced..0bd38c435 100644 --- a/aegisub/src/spline_curve.h +++ b/aegisub/src/spline_curve.h @@ -34,57 +34,54 @@ /// @ingroup visual_ts /// -/////////// -// Headers #include "vector2d.h" -/// DOCME -enum CurveType { - - /// DOCME - CURVE_INVALID, - - /// DOCME - CURVE_POINT, - - /// DOCME - CURVE_LINE, - - /// DOCME - CURVE_BICUBIC -}; - /// DOCME /// @class SplineCurve /// @brief DOCME /// /// DOCME class SplineCurve { -private: - float GetClosestSegmentPart(Vector2D const& p1,Vector2D const& p2,Vector2D const& p3) const; - float GetClosestSegmentDistance(Vector2D const& p1,Vector2D const& p2,Vector2D const& p3) const; + /// Closest t in segment p1-p2 to point p3 + float GetClosestSegmentPart(Vector2D p1, Vector2D p2, Vector2D p3) const; + /// Closest distance between p3 and segment p1-p2 + float GetClosestSegmentDistance(Vector2D p1, Vector2D p2, Vector2D p3) const; public: + enum CurveType { + POINT, + LINE, + BICUBIC + }; - /// DOCME - - /// DOCME - - /// DOCME - - /// DOCME - Vector2D p1,p2,p3,p4; + Vector2D p1; + Vector2D p2; + Vector2D p3; + Vector2D p4; /// DOCME CurveType type; - SplineCurve(); - void Split(SplineCurve &c1,SplineCurve &c2,float t=0.5); - void Smooth(Vector2D const& prev,Vector2D const& next,float smooth=1.0f); + SplineCurve(Vector2D p1 = Vector2D()); + SplineCurve(Vector2D p1, Vector2D p2); + SplineCurve(Vector2D p1, Vector2D p2, Vector2D p3, Vector2D p4); + + /// @brief Split a curve in two using the de Casteljau algorithm + /// @param[out] c1 Curve before split point + /// @param[out] c2 Curve after split point + /// @param t Split point + void Split(SplineCurve &c1, SplineCurve &c2, float t = 0.5f); + + /// @brief Smooths the curve + /// @note Based on http://antigrain.com/research/bezier_interpolation/index.html + void Smooth(Vector2D prev, Vector2D next, float smooth = 1.0f); Vector2D GetPoint(float t) const; Vector2D& EndPoint(); - Vector2D GetClosestPoint(Vector2D const& ref) const; - float GetClosestParam(Vector2D const& ref) const; - float GetQuickDistance(Vector2D const& ref) const; + /// Get point on the curve closest to reference + Vector2D GetClosestPoint(Vector2D ref) const; + /// Get t value for the closest point to reference + float GetClosestParam(Vector2D ref) const; + /// Get distance from ref to the closest point on the curve + float GetQuickDistance(Vector2D ref) const; }; diff --git a/aegisub/src/subs_edit_box.cpp b/aegisub/src/subs_edit_box.cpp index d5de9e976..63600a8a6 100644 --- a/aegisub/src/subs_edit_box.cpp +++ b/aegisub/src/subs_edit_box.cpp @@ -74,7 +74,6 @@ #include "utils.h" #include "validators.h" #include "video_context.h" -#include "video_display.h" enum { BUTTON_BOLD = 1300, diff --git a/aegisub/src/toolbar.cpp b/aegisub/src/toolbar.cpp index 896d0362a..9f8846f6f 100644 --- a/aegisub/src/toolbar.cpp +++ b/aegisub/src/toolbar.cpp @@ -166,8 +166,8 @@ namespace { } public: - Toolbar(wxWindow *parent, std::string const& name, agi::Context *c, std::string const& ht_context) - : wxToolBar(parent, -1, wxDefaultPosition, wxDefaultSize, wxTB_FLAT | wxTB_HORIZONTAL) + Toolbar(wxWindow *parent, std::string const& name, agi::Context *c, std::string const& ht_context, bool vertical) + : wxToolBar(parent, -1, wxDefaultPosition, wxDefaultSize, wxTB_FLAT | (vertical ? wxTB_VERTICAL : wxTB_HORIZONTAL)) , name(name) , context(c) , ht_context(ht_context) @@ -182,10 +182,10 @@ namespace { namespace toolbar { void AttachToolbar(wxFrame *frame, std::string const& name, agi::Context *c, std::string const& hotkey) { - frame->SetToolBar(new Toolbar(frame, name, c, hotkey)); + frame->SetToolBar(new Toolbar(frame, name, c, hotkey, false)); } - wxToolBar *GetToolbar(wxWindow *parent, std::string const& name, agi::Context *c, std::string const& hotkey) { - return new Toolbar(parent, name, c, hotkey); + wxToolBar *GetToolbar(wxWindow *parent, std::string const& name, agi::Context *c, std::string const& hotkey, bool vertical) { + return new Toolbar(parent, name, c, hotkey, vertical); } } diff --git a/aegisub/src/vector2d.cpp b/aegisub/src/vector2d.cpp index b83754c8e..66ff49c08 100644 --- a/aegisub/src/vector2d.cpp +++ b/aegisub/src/vector2d.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -34,238 +21,77 @@ /// @ingroup utility visual_ts /// - -/////////// -// Headers #include "config.h" -#ifndef AGI_PRE -#include -#endif - #include "vector2d.h" +#ifndef AGI_PRE +#include -/// @brief Null constructor -/// -Vector2D::Vector2D () { - x = y = 0; +#include +#endif + +Vector2D operator *(float f, Vector2D v) { + return Vector2D(v.X() * f, v.Y() * f); } - - -/// @brief Standard constructor -/// @param _x -/// @param _y -/// -Vector2D::Vector2D (float _x,float _y) { - x = _x; - y = _y; +Vector2D operator /(float f, Vector2D v) { + return Vector2D(f / v.X(), f / v.Y()); } - - -/// @brief Construction from another vector -/// @param vec -/// -Vector2D::Vector2D (const Vector2D &vec) { - x = vec.x; - y = vec.y; +Vector2D operator +(float f, Vector2D v) { + return Vector2D(v.X() + f, v.Y() + f); } - - -/// @brief Assignment -/// @param param -/// -void Vector2D::operator = (const Vector2D param) { - x = param.x; - y = param.y; +Vector2D operator -(float f, Vector2D v) { + return Vector2D(f - v.X(), f - v.Y()); } - - -/// @brief Comparison -/// @param param -/// @return -/// -bool Vector2D::operator == (const Vector2D param) const { - return ((x == param.x) && (y == param.y)); +Vector2D Vector2D::Unit() const { + float len = Len(); + if (len == 0) + return Vector2D(0, 0); + return *this / len; } - -/// @brief DOCME -/// @param param -/// @return -/// -bool Vector2D::operator != (const Vector2D param) const { - return ((x != param.x) || (y == param.y)); +Vector2D Vector2D::SingleAxis() const { + if (abs(x) < abs(y)) + return Vector2D(0, y); + else + return Vector2D(x, 0); } - - -/// @brief Adition -/// @param param -/// @return -/// -Vector2D Vector2D::operator + (const Vector2D param) const { - return Vector2D(x + param.x,y + param.y); +Vector2D Vector2D::Max(Vector2D param) const { + return Vector2D(std::max(x, param.x), std::max(y, param.y)); } - -/// @brief DOCME -/// @param param -/// @return -/// -Vector2D Vector2D::operator += (const Vector2D param) { - x += param.x; - y += param.y; - return *this; +Vector2D Vector2D::Min(Vector2D param) const { + return Vector2D(std::min(x, param.x), std::min(y, param.y)); } - - -/// @brief Subtraction -/// @param param -/// @return -/// -Vector2D Vector2D::operator - (const Vector2D param) const { - return Vector2D(x - param.x,y - param.y); +Vector2D Vector2D::Round(float step) const { + return Vector2D(floorf(x / step + .5f) * step, floorf(y / step + .5f) * step); } - -/// @brief DOCME -/// @param param -/// @return -/// -Vector2D Vector2D::operator -= (const Vector2D param) { - x -= param.x; - y -= param.y; - return *this; +Vector2D::operator unspecified_bool_type() const { + return *this == Bad() ? 0 : &Vector2D::x; } - - -/// @brief Negate -/// @return -/// -Vector2D Vector2D::operator - () const { - return Vector2D(-x,-y); +Vector2D Vector2D::Bad() { + return Vector2D(std::numeric_limits::min(), std::numeric_limits::min()); } - - -/// @brief Multiplication by scalar -/// @param param -/// @return -/// -Vector2D Vector2D::operator * (float param) const { - return Vector2D(x * param,y * param); +wxString Vector2D::PStr(char sep) const { + return "(" + Str(sep) + ")"; } - -/// @brief DOCME -/// @param param -/// @return -/// -Vector2D Vector2D::operator *= (float param) { - x *= param; - y *= param; - return *this; +wxString Vector2D::DStr(char sep) const { + return wxString::Format("%d%c%d", (int)x, sep, (int)y); } - -/// @brief DOCME -/// @param f -/// @param v -/// @return -/// -Vector2D operator * (float f,const Vector2D &v) { - return Vector2D(v.x * f,v.y * f); +wxString Vector2D::Str(char sep) const { + return + wxNumberFormatter::ToString(x, 3, wxNumberFormatter::Style_NoTrailingZeroes) + + sep + + wxNumberFormatter::ToString(y, 3, wxNumberFormatter::Style_NoTrailingZeroes); } - - - -/// @brief Division by scalar -/// @param param -/// @return -/// -Vector2D Vector2D::operator / (float param) const { - return Vector2D(x / param,y / param); -} - - -/// @brief DOCME -/// @param param -/// @return -/// -Vector2D Vector2D::operator /= (float param) { - x /= param; - y /= param; - return *this; -} - - -/// @brief DOCME -/// @param f -/// @param v -/// @return -/// -Vector2D operator / (float f,const Vector2D &v) { - return Vector2D(v.x / f,v.y / f); -} - - - -/// @brief Cross product -/// @param param -/// @return -/// -float Vector2D::Cross (const Vector2D param) const { - return x * param.y - y * param.x; -} - - - -/// @brief Dot product -/// @param param -/// @return -/// -float Vector2D::Dot (const Vector2D param) const { - return (x * param.x) + (y * param.y); -} - - - -/// @brief Length -/// @return -/// -float Vector2D::Len () const { - return sqrt(x*x + y*y); -} - - - -/// @brief Squared Length -/// @return -/// -float Vector2D::SquareLen () const { - return x*x + y*y; -} - - - -/// @brief Unitary -/// -Vector2D Vector2D::Unit () const { - float l = Len(); - if (l != 0) { - Vector2D temp; - temp.x = x; - temp.y = y; - return temp / l; - } - else return Vector2D(0,0); -} - - diff --git a/aegisub/src/vector2d.h b/aegisub/src/vector2d.h index 147f5dd95..7301477e8 100644 --- a/aegisub/src/vector2d.h +++ b/aegisub/src/vector2d.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -34,8 +21,13 @@ /// @ingroup utility visual_ts /// +#pragma once +#ifndef AGI_PRE +#include +#include +#endif /// DOCME /// @class Vector2D @@ -43,53 +35,62 @@ /// /// DOCME class Vector2D { + float x, y; + + typedef float Vector2D::*unspecified_bool_type; public: + float X() const { return x; } + float Y() const { return y; } - /// DOCME + Vector2D() : x(0), y(0) { } + Vector2D(float x, float y) : x(x), y(y) { } + Vector2D(wxPoint pt) : x(pt.x), y(pt.y) { } + Vector2D(Vector2D x, Vector2D y) : x(x.x), y(y.y) { } + Vector2D(float x, Vector2D y) : x(x), y(y.y) { } + Vector2D(Vector2D x, float y) : x(x.x), y(y) { } - /// DOCME - float x,y; + bool operator ==(const Vector2D r) const { return x == r.x && y == r.y; } + bool operator !=(const Vector2D r) const { return x != r.x || y != r.y; } + operator unspecified_bool_type() const; - Vector2D (); - Vector2D (float _x,float _y); - Vector2D (const Vector2D &vec); + Vector2D operator -() const { return Vector2D(-x, -y); } + Vector2D operator +(const Vector2D r) const { return Vector2D(x + r.x, y + r.y); } + Vector2D operator -(const Vector2D r) const { return Vector2D(x - r.x, y - r.y); } + Vector2D operator *(const Vector2D r) const { return Vector2D(x * r.x, y * r.y); } + Vector2D operator /(const Vector2D r) const { return Vector2D(x / r.x, y / r.y); } + Vector2D operator +(float param) const { return Vector2D(x + param, y + param); } + Vector2D operator -(float param) const { return Vector2D(x - param, y - param); } + Vector2D operator *(float param) const { return Vector2D(x * param, y * param); } + Vector2D operator /(float param) const { return Vector2D(x / param, y / param); } - void operator = (const Vector2D param); - bool operator == (const Vector2D param) const; - bool operator != (const Vector2D param) const; + Vector2D Unit() const; + Vector2D SingleAxis() const; - Vector2D operator - () const; - Vector2D operator + (const Vector2D param) const; - Vector2D operator - (const Vector2D param) const; - Vector2D operator * (float param) const; - Vector2D operator / (float param) const; + Vector2D Perpendicular() const { return Vector2D(-y, x); } - Vector2D operator += (const Vector2D param); - Vector2D operator -= (const Vector2D param); - Vector2D operator *= (float param); - Vector2D operator /= (float param); + Vector2D Max(Vector2D param) const; + Vector2D Min(Vector2D param) const; + Vector2D Round(float step) const; - Vector2D Unit () const; - float Cross (const Vector2D param) const; - float Dot (const Vector2D param) const; + float Cross(const Vector2D param) const { return x * param.y - y * param.x; } + float Dot(const Vector2D param) const { return x * param.x + y * param.y; } - float Len () const; + float Len() const { return sqrt(x*x + y*y); } + float SquareLen() const { return x*x + y*y; } + float Angle() const { return atan2(y, x); } - /// @brief DOCME - /// @return - /// - float Length () const { return Len(); } - float SquareLen () const; + /// Get as string with given separator + wxString Str(char sep = ',') const; + /// Get as string surrounded by parentheses with given separator + wxString PStr(char sep = ',') const; + /// Get as string with given separator with values rounded to ints + wxString DStr(char sep = ',') const; - /// @brief DOCME - /// - float SquareLength () const { return SquareLen(); } + static Vector2D FromAngle(float angle) { return Vector2D(cos(-angle), sin(-angle)); } + static Vector2D Bad(); }; - -//////////////////// -// Global operators -Vector2D operator * (float f,const Vector2D &v); -Vector2D operator / (float f,const Vector2D &v); - - +Vector2D operator * (float f, Vector2D v); +Vector2D operator / (float f, Vector2D v); +Vector2D operator + (float f, Vector2D v); +Vector2D operator - (float f, Vector2D v); diff --git a/aegisub/src/video_box.cpp b/aegisub/src/video_box.cpp index a5189178a..3bc133d83 100644 --- a/aegisub/src/video_box.cpp +++ b/aegisub/src/video_box.cpp @@ -44,6 +44,7 @@ #endif #include "include/aegisub/context.h" +#include "include/aegisub/toolbar.h" #include "ass_dialogue.h" #include "ass_file.h" @@ -104,22 +105,11 @@ VideoBox::VideoBox(wxWindow *parent, bool isDetached, agi::Context *context) zoomBox = new wxComboBox(this, -1, "75%", wxDefaultPosition, wxDefaultSize, choices, wxCB_DROPDOWN); // Typesetting buttons - visualToolBar = new wxToolBar(this,-1,wxDefaultPosition,wxDefaultSize,wxTB_VERTICAL|wxTB_FLAT|wxTB_NODIVIDER); - visualToolBar->AddTool(Video_Mode_Standard,_("Standard"),GETIMAGE(visual_standard_24),_("Standard mode, double click sets position."),wxITEM_RADIO); - visualToolBar->AddTool(Video_Mode_Drag,_("Drag"),GETIMAGE(visual_move_24),_("Drag subtitles."),wxITEM_RADIO); - visualToolBar->AddTool(Video_Mode_Rotate_Z,_("Rotate Z"),GETIMAGE(visual_rotatez_24),_("Rotate subtitles on their Z axis."),wxITEM_RADIO); - visualToolBar->AddTool(Video_Mode_Rotate_XY,_("Rotate XY"),GETIMAGE(visual_rotatexy_24),_("Rotate subtitles on their X and Y axes."),wxITEM_RADIO); - visualToolBar->AddTool(Video_Mode_Scale,_("Scale"),GETIMAGE(visual_scale_24),_("Scale subtitles on X and Y axes."),wxITEM_RADIO); - visualToolBar->AddTool(Video_Mode_Clip,_("Clip"),GETIMAGE(visual_clip_24),_("Clip subtitles to a rectangle."),wxITEM_RADIO); - visualToolBar->AddTool(Video_Mode_Vector_Clip,_("Vector Clip"),GETIMAGE(visual_vector_clip_24),_("Clip subtitles to a vectorial area."),wxITEM_RADIO); - visualToolBar->AddSeparator(); - visualToolBar->AddTool(wxID_HELP,_("Help"),cmd::get("help/video")->Icon(24),_("Open the manual page for Visual Typesetting.")); - visualToolBar->Realize(); + visualToolBar = toolbar::GetToolbar(this, "visual_tools", context, "Video", true); // Avoid ugly themed background on Vista and possibly also Win7 visualToolBar->SetBackgroundStyle(wxBG_STYLE_COLOUR); visualToolBar->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); - // Display videoDisplay = new VideoDisplay(this,isDetached,zoomBox,this,context); // Top sizer diff --git a/aegisub/src/video_box.h b/aegisub/src/video_box.h index 1899ac80a..e3d0e8cc7 100644 --- a/aegisub/src/video_box.h +++ b/aegisub/src/video_box.h @@ -92,14 +92,3 @@ public: VideoBox(wxWindow *parent, bool isDetached, agi::Context *context); ~VideoBox(); }; - -// IDs -enum { - Video_Mode_Standard = 5000, - Video_Mode_Drag, - Video_Mode_Rotate_Z, - Video_Mode_Rotate_XY, - Video_Mode_Scale, - Video_Mode_Clip, - Video_Mode_Vector_Clip, -}; diff --git a/aegisub/src/video_context.cpp b/aegisub/src/video_context.cpp index 79cb1c16e..61c0b8d00 100644 --- a/aegisub/src/video_context.cpp +++ b/aegisub/src/video_context.cpp @@ -266,7 +266,8 @@ void VideoContext::JumpToFrame(int n) { frame_n = mid(0, n, GetLength() - 1); - Seek(n); + GetFrameAsync(frame_n); + Seek(frame_n); } void VideoContext::JumpToTime(int ms, agi::vfr::Time end) { @@ -274,11 +275,11 @@ void VideoContext::JumpToTime(int ms, agi::vfr::Time end) { } void VideoContext::GetFrameAsync(int n) { - provider->RequestFrame(n,videoFPS.TimeAtFrame(n)/1000.0); + provider->RequestFrame(n, videoFPS.TimeAtFrame(n) / 1000.0); } std::tr1::shared_ptr VideoContext::GetFrame(int n, bool raw) { - return provider->GetFrame(n, videoFPS.TimeAtFrame(n)/1000.0, raw); + return provider->GetFrame(n, videoFPS.TimeAtFrame(n) / 1000.0, raw); } int VideoContext::GetWidth() const { @@ -326,10 +327,6 @@ void VideoContext::SaveSnapshot(bool raw) { GetFrame(frame_n,raw)->GetImage().SaveFile(path,wxBITMAP_TYPE_PNG); } -void VideoContext::GetScriptSize(int &sw,int &sh) { - context->ass->GetResolution(sw,sh); -} - void VideoContext::NextFrame() { if (!videoProvider.get() || isPlaying || frame_n == videoProvider->GetFrameCount()) return; diff --git a/aegisub/src/video_context.h b/aegisub/src/video_context.h index c65491de7..56f9165bd 100644 --- a/aegisub/src/video_context.h +++ b/aegisub/src/video_context.h @@ -232,13 +232,6 @@ public: /// @param end Type of time void JumpToTime(int ms, agi::vfr::Time end = agi::vfr::START); - /// @brief Get the height and width of the current script - /// @param[out] w Width - /// @param[out] h Height - /// - /// This probably shouldn't be in VideoContext - void GetScriptSize(int &w,int &h); - /// Starting playing the video void Play(); /// Play the next frame then stop diff --git a/aegisub/src/video_display.cpp b/aegisub/src/video_display.cpp index 3be8866b1..fe61828a9 100644 --- a/aegisub/src/video_display.cpp +++ b/aegisub/src/video_display.cpp @@ -56,13 +56,13 @@ #include #endif -#include "include/aegisub/context.h" -#include "include/aegisub/hotkey.h" -#include "include/aegisub/menu.h" - #include "video_display.h" #include "ass_file.h" +#include "command/command.h" +#include "include/aegisub/context.h" +#include "include/aegisub/hotkey.h" +#include "include/aegisub/menu.h" #include "main.h" #include "threaded_frame_source.h" #include "utils.h" @@ -70,13 +70,6 @@ #include "video_box.h" #include "video_context.h" #include "visual_tool.h" -#include "visual_tool_clip.h" -#include "visual_tool_cross.h" -#include "visual_tool_drag.h" -#include "visual_tool_rotatexy.h" -#include "visual_tool_rotatez.h" -#include "visual_tool_scale.h" -#include "visual_tool_vector_clip.h" /// Attribute list for gl canvases; set the canvases to doublebuffered rgba with an 8 bit stencil buffer int attribList[] = { WX_GL_RGBA , WX_GL_DOUBLEBUFFER, WX_GL_STENCIL_SIZE, 8, 0 }; @@ -104,14 +97,17 @@ VideoDisplay::VideoDisplay( : wxGLCanvas (parent, -1, attribList, wxDefaultPosition, wxDefaultSize, 0, wxPanelNameStr) , alwaysShowTools(OPT_GET("Tool/Visual/Always Show")) , con(c) -, currentFrame(-1) -, w(8), h(8), viewport_x(0), viewport_width(0), viewport_bottom(0), viewport_top(0), viewport_height(0) +, w(8) +, h(8) +, mouse_pos(Vector2D::Bad()) +, viewport_left(0) +, viewport_width(0) +, viewport_bottom(0) +, viewport_top(0) +, viewport_height(0) , zoomValue(OPT_GET("Video/Default Zoom")->GetInt() * .125 + .125) , videoOut(new VideoOutGL()) -, activeMode(Video_Mode_Standard) , toolBar(box->visualSubToolBar) -, scriptW(INT_MIN) -, scriptH(INT_MIN) , zoomBox(zoomBox) , box(box) , freeSize(freeSize) @@ -120,34 +116,29 @@ VideoDisplay::VideoDisplay( zoomBox->SetValue(wxString::Format("%g%%", zoomValue * 100.)); zoomBox->Bind(wxEVT_COMMAND_COMBOBOX_SELECTED, &VideoDisplay::SetZoomFromBox, this); - box->Bind(wxEVT_COMMAND_TOOL_CLICKED, &VideoDisplay::OnMode, this, Video_Mode_Standard, Video_Mode_Vector_Clip); con->videoController->Bind(EVT_FRAME_READY, &VideoDisplay::UploadFrameData, this); - slots.push_back(con->videoController->AddSeekListener(&VideoDisplay::SetFrame, this)); slots.push_back(con->videoController->AddVideoOpenListener(&VideoDisplay::OnVideoOpen, this)); slots.push_back(con->videoController->AddARChangeListener(&VideoDisplay::UpdateSize, this)); - slots.push_back(con->ass->AddCommitListener(&VideoDisplay::OnCommit, this)); Bind(wxEVT_PAINT, std::tr1::bind(&VideoDisplay::Render, this)); if (freeSize) { Bind(wxEVT_SIZE, &VideoDisplay::OnSizeEvent, this); } Bind(wxEVT_CONTEXT_MENU, &VideoDisplay::OnContextMenu, this); + Bind(wxEVT_ENTER_WINDOW, &VideoDisplay::OnMouseEvent, this); Bind(wxEVT_KEY_DOWN, &VideoDisplay::OnKeyDown, this); + Bind(wxEVT_LEAVE_WINDOW, &VideoDisplay::OnMouseLeave, this); Bind(wxEVT_LEFT_DCLICK, &VideoDisplay::OnMouseEvent, this); Bind(wxEVT_LEFT_DOWN, &VideoDisplay::OnMouseEvent, this); Bind(wxEVT_LEFT_UP, &VideoDisplay::OnMouseEvent, this); Bind(wxEVT_MOTION, &VideoDisplay::OnMouseEvent, this); - Bind(wxEVT_ENTER_WINDOW, &VideoDisplay::OnMouseEnter, this); - Bind(wxEVT_LEAVE_WINDOW, &VideoDisplay::OnMouseLeave, this); Bind(wxEVT_MOUSEWHEEL, &VideoDisplay::OnMouseWheel, this); SetCursor(wxNullCursor); - if (con->videoController->IsLoaded()) { - con->videoController->GetScriptSize(scriptW, scriptH); + if (con->videoController->IsLoaded()) OnVideoOpen(); - } } VideoDisplay::~VideoDisplay () { @@ -163,25 +154,6 @@ bool VideoDisplay::InitContext() { return true; } -void VideoDisplay::ShowCursor(bool show) { - if (show) { - SetCursor(wxNullCursor); - } - else { - SetCursor(wxCursor(wxCURSOR_BLANK)); - } -} - -void VideoDisplay::SetFrame(int frameNumber) { - currentFrame = frameNumber; - - // Render the new frame - if (con->videoController->IsLoaded()) { - tool->SetFrame(frameNumber); - con->videoController->GetFrameAsync(currentFrame); - } -} - void VideoDisplay::UploadFrameData(FrameReadyEvent &evt) { if (!InitContext()) return; @@ -207,16 +179,10 @@ void VideoDisplay::UploadFrameData(FrameReadyEvent &evt) { void VideoDisplay::OnVideoOpen() { if (!con->videoController->IsLoaded()) return; + if (!tool.get()) + cmd::call("video/tool/cross", con); UpdateSize(); - if (!tool.get()) tool.reset(new VisualToolCross(this, con, video, toolBar)); - SetFrame(0); - tool->Refresh(); -} - -void VideoDisplay::OnCommit(int type) { - if (type == AssFile::COMMIT_NEW || type & AssFile::COMMIT_SCRIPTINFO) - con->videoController->GetScriptSize(scriptW, scriptH); - if (tool.get()) tool->Refresh(); + con->videoController->JumpToFrame(0); } void VideoDisplay::Render() try { @@ -225,7 +191,7 @@ void VideoDisplay::Render() try { assert(wxIsMainThread()); if (!viewport_height || !viewport_width) UpdateSize(); - videoOut->Render(viewport_x, viewport_bottom, viewport_width, viewport_height); + videoOut->Render(viewport_left, viewport_bottom, viewport_width, viewport_height); E(glViewport(0, std::min(viewport_bottom, 0), w, h)); E(glMatrixMode(GL_PROJECTION)); @@ -238,73 +204,55 @@ void VideoDisplay::Render() try { // Based on BBC's guidelines: http://www.bbc.co.uk/guidelines/dq/pdf/tv/tv_standards_london.pdf // 16:9 or wider if (ar > 1.75) { - DrawOverscanMask(w * 0.1, h * 0.05, wxColor(30,70,200),0.5); - DrawOverscanMask(w * 0.035, h * 0.035, wxColor(30,70,200),0.5); + DrawOverscanMask(.1f, .05f); + DrawOverscanMask(0.035f, 0.035f); } - // Less wide than 16:9 (use 4:3 standard) else { - DrawOverscanMask(w * 0.067, h * 0.05, wxColor(30,70,200),0.5); - DrawOverscanMask(w * 0.033, h * 0.035, wxColor(30,70,200),0.5); + DrawOverscanMask(.067f, .05f); + DrawOverscanMask(0.033f, 0.035f); } } - if (video.x > INT_MIN || video.y > INT_MIN || alwaysShowTools->GetBool()) { + if (mouse_pos || alwaysShowTools->GetBool()) { if (!con->videoController->IsPlaying()) tool->Draw(); } SwapBuffers(); } -catch (const VideoOutException &err) { +catch (const agi::Exception &err) { wxLogError( "An error occurred trying to render the video frame on the screen.\n" "Error message reported: %s", - err.GetMessage()); - con->videoController->Reset(); -} -catch (const OpenGlException &err) { - wxLogError( - "An error occurred trying to render visual overlays on the screen.\n" - "Error message reported: %s", - err.GetMessage()); - con->videoController->Reset(); -} -catch (const char *err) { - wxLogError( - "An error occurred trying to render the video frame on the screen.\n" - "Error message reported: %s", - err); - con->videoController->Reset(); -} -catch (...) { - wxLogError( - "An error occurred trying to render the video frame to screen.\n" - "No further error message given."); + err.GetChainedMessage()); con->videoController->Reset(); } -void VideoDisplay::DrawOverscanMask(int sizeH, int sizeV, wxColor color, double alpha) const { +void VideoDisplay::DrawOverscanMask(float horizontal_percent, float vertical_percent) const { + Vector2D size(w * horizontal_percent / 2, h * vertical_percent / 2); int rad1 = h * 0.05; - int gapH = sizeH+rad1; - int gapV = sizeV+rad1; - int rad2 = sqrt(double(gapH*gapH + gapV*gapV)) + 1; + Vector2D gap = size + rad1; + int rad2 = gap.Len() + 1; + Vector2D v(w, h); + Vector2D igap = v - gap; + Vector2D isize = v - size; OpenGLWrapper gl; - E(gl.SetFillColour(color, alpha)); - gl.SetLineColour(wxColor(0, 0, 0), 0.0, 1); + gl.SetFillColour(wxColor(30, 70, 200), .5f); + gl.SetLineColour(*wxBLACK, 0, 1); // Draw sides - E(gl.DrawRectangle(gapH, 0, w-gapH, sizeV)); // Top - E(gl.DrawRectangle(w-sizeH, gapV, w, h-gapV)); // Right - E(gl.DrawRectangle(gapH, h-sizeV, w-gapH, h)); // Bottom - E(gl.DrawRectangle(0, gapV, sizeH, h-gapV)); // Left + gl.DrawRectangle(Vector2D(gap, 0), Vector2D(igap, size)); // Top + gl.DrawRectangle(Vector2D(isize, gap), Vector2D(v, igap)); // Right + gl.DrawRectangle(Vector2D(gap, isize), Vector2D(igap, v)); // Bottom + gl.DrawRectangle(Vector2D(0, gap), Vector2D(size, igap)); // Left // Draw rounded corners - E(gl.DrawRing(gapH, gapV, rad1, rad2, 1.0, 180.0, 270.0)); // Top-left - E(gl.DrawRing(w-gapH, gapV, rad1, rad2, 1.0, 90.0, 180.0)); // Top-right - E(gl.DrawRing(w-gapH, h-gapV, rad1, rad2, 1.0, 0.0, 90.0)); // Bottom-right - E(gl.DrawRing(gapH, h-gapV, rad1, rad2, 1.0,270.0,360.0)); // Bottom-left + gl.DrawRing(gap, rad1, rad2, 1.f, 90.f, 180.f); // Top-left + gl.DrawRing(Vector2D(igap, gap), rad1, rad2, 1.f, 0.f, 90.f); // Top-right + gl.DrawRing(v - gap, rad1, rad2, 1.f, 270.f, 360.f); // Bottom-right + gl.DrawRing(Vector2D(gap, igap), rad1, rad2, 1.f, 180.f, 270.f); // Bottom-left E(glDisable(GL_BLEND)); } @@ -323,7 +271,7 @@ void VideoDisplay::UpdateSize(int arType, double arValue) { if (freeSize) { GetClientSize(&w,&h); - viewport_x = 0; + viewport_left = 0; viewport_bottom = 0; viewport_top = 0; viewport_width = w; @@ -336,7 +284,7 @@ void VideoDisplay::UpdateSize(int arType, double arValue) { // Window is wider than video, blackbox left/right if (displayAr - videoAr > 0.01f) { int delta = w - videoAr * h; - viewport_x = delta / 2; + viewport_left = delta / 2; viewport_width = w - delta; } @@ -359,7 +307,7 @@ void VideoDisplay::UpdateSize(int arType, double arValue) { // Cap the canvas size to the window size int cw = std::min(w, maxW), ch = std::min(h, maxH); - viewport_x = 0; + viewport_left = 0; viewport_bottom = ch - h; viewport_top = 0; viewport_width = w; @@ -380,11 +328,8 @@ void VideoDisplay::UpdateSize(int arType, double arValue) { SetEvtHandlerEnabled(true); } - con->videoController->GetScriptSize(scriptW, scriptH); - video.w = w; - video.h = h; - - if (tool.get()) tool->Refresh(); + if (tool.get()) + tool->SetDisplayArea(viewport_left, viewport_top, viewport_width, viewport_height); Refresh(false); } @@ -403,20 +348,13 @@ void VideoDisplay::OnMouseEvent(wxMouseEvent& event) { if (event.ButtonDown()) SetFocus(); - video.x = event.GetX(); - video.y = event.GetY(); + mouse_pos = event.GetPosition(); tool->OnMouseEvent(event); } -void VideoDisplay::OnMouseEnter(wxMouseEvent& event) { - ShowCursor(activeMode != Video_Mode_Standard); - tool->OnMouseEvent(event); -} - void VideoDisplay::OnMouseLeave(wxMouseEvent& event) { - video.x = INT_MIN; - video.y = INT_MIN; + mouse_pos = Vector2D::Bad(); tool->OnMouseEvent(event); } @@ -429,33 +367,22 @@ void VideoDisplay::OnMouseWheel(wxMouseEvent& event) { void VideoDisplay::OnContextMenu(wxContextMenuEvent&) { if (!context_menu.get()) context_menu.reset(menu::GetMenu("video_context", con)); - ShowCursor(true); + SetCursor(wxNullCursor); menu::OpenPopupMenu(context_menu.get(), this); } void VideoDisplay::OnKeyDown(wxKeyEvent &event) { - /// @todo - int kc = event.GetKeyCode(); - if (kc == 'A') SetMode(Video_Mode_Standard); - else if (kc == 'S') SetMode(Video_Mode_Drag); - else if (kc == 'D') SetMode(Video_Mode_Rotate_Z); - else if (kc == 'F') SetMode(Video_Mode_Rotate_XY); - else if (kc == 'G') SetMode(Video_Mode_Scale); - else if (kc == 'H') SetMode(Video_Mode_Clip); - else if (kc == 'J') SetMode(Video_Mode_Vector_Clip); - else { - event.StopPropagation(); - if (hotkey::check("Video Display", con, event.GetKeyCode(), event.GetUnicodeKey(), event.GetModifiers())) - return; - } + event.StopPropagation(); + if (hotkey::check("Video", con, event.GetKeyCode(), event.GetUnicodeKey(), event.GetModifiers())) + return; } - void VideoDisplay::SetZoom(double value) { zoomValue = std::max(value, .125); zoomBox->SetValue(wxString::Format("%g%%", zoomValue * 100.)); UpdateSize(); } + void VideoDisplay::SetZoomFromBox(wxCommandEvent &) { wxString strValue = zoomBox->GetValue(); strValue.EndsWith("%", &strValue); @@ -466,57 +393,19 @@ void VideoDisplay::SetZoomFromBox(wxCommandEvent &) { } } -template -void VideoDisplay::SetTool() { - tool.reset(); - tool.reset(new T(this, con, video, toolBar)); - box->Bind(wxEVT_COMMAND_TOOL_CLICKED, &T::OnSubTool, static_cast(tool.get()), VISUAL_SUB_TOOL_START, VISUAL_SUB_TOOL_END); -} -void VideoDisplay::OnMode(const wxCommandEvent &event) { - SetMode(event.GetId()); -} -void VideoDisplay::SetMode(int mode) { - if (activeMode == mode) return; - +void VideoDisplay::SetTool(VisualToolBase *new_tool) { toolBar->ClearTools(); toolBar->Realize(); toolBar->Show(false); - if (!box->visualToolBar->GetToolState(mode)) { - box->visualToolBar->ToggleTool(mode, true); - } - - activeMode = mode; - switch (mode) { - case Video_Mode_Standard: SetTool(); break; - case Video_Mode_Drag: SetTool(); break; - case Video_Mode_Rotate_Z: SetTool(); break; - case Video_Mode_Rotate_XY: SetTool(); break; - case Video_Mode_Scale: SetTool(); break; - case Video_Mode_Clip: SetTool(); break; - case Video_Mode_Vector_Clip: SetTool(); break; - default: assert(false); break; - } + tool.reset(new_tool); + tool->SetToolbar(toolBar); + tool->SetDisplayArea(viewport_left, viewport_top, viewport_width, viewport_height); // Update size as the new typesetting tool may have changed the subtoolbar size UpdateSize(); - ShowCursor(activeMode != Video_Mode_Standard); } -void VideoDisplay::ToScriptCoords(int *x, int *y) const { - int sx = *x - viewport_x > 0 ? viewport_width : -viewport_width; - int sy = *y - viewport_top > 0 ? viewport_height : -viewport_height; - *x = ((*x - viewport_x) * scriptW + sx / 2) / viewport_width; - *y = ((*y - viewport_top) * scriptH + sy / 2) / viewport_height; -} -void VideoDisplay::FromScriptCoords(int *x, int *y) const { - int sx = *x > 0 ? scriptW : -scriptW; - int sy = *y > 0 ? scriptH : -scriptH; - *x = (*x * viewport_width + sx / 2) / scriptW + viewport_x; - *y = (*y * viewport_height + sy / 2) / scriptH + viewport_top; -} - -void VideoDisplay::GetMousePosition(int *x, int *y) const { - *x = video.x; - *y = video.y; +Vector2D VideoDisplay::GetMousePosition() const { + return mouse_pos ? tool->ToScriptCoords(mouse_pos) : mouse_pos; } diff --git a/aegisub/src/video_display.h b/aegisub/src/video_display.h index b39460e86..1942a796a 100644 --- a/aegisub/src/video_display.h +++ b/aegisub/src/video_display.h @@ -44,12 +44,14 @@ #include #include +#include "vector2d.h" + // Prototypes class FrameReadyEvent; class VideoBox; class VideoContext; class VideoOutGL; -class IVisualTool; +class VisualToolBase; class wxComboBox; class wxTextCtrl; class wxToolBar; @@ -59,14 +61,6 @@ namespace agi { class OptionValue; } -struct VideoState { - int x; - int y; - int w; - int h; - VideoState() : x(INT_MIN), y(INT_MIN), w(INT_MIN), h(INT_MIN) { } -}; - /// @class VideoDisplay /// @brief DOCME class VideoDisplay : public wxGLCanvas { @@ -87,8 +81,10 @@ class VideoDisplay : public wxGLCanvas { /// The height of the canvas in screen pixels int h; + Vector2D mouse_pos; + /// Screen pixels between the left of the canvas and the left of the video - int viewport_x; + int viewport_left; /// The width of the video in screen pixels int viewport_width; /// Screen pixels between the bottom of the canvas and the bottom of the video; used for glViewport @@ -105,22 +101,13 @@ class VideoDisplay : public wxGLCanvas { agi::scoped_ptr videoOut; /// The active visual typesetting tool - agi::scoped_ptr tool; - /// The current tool's ID - int activeMode; + agi::scoped_ptr tool; /// The toolbar used by individual typesetting tools wxToolBar* toolBar; /// The OpenGL context for this display agi::scoped_ptr glContext; - /// The current script width - int scriptW; - /// The current script height - int scriptH; - - VideoState video; - /// The dropdown box for selecting zoom levels wxComboBox *zoomBox; @@ -131,11 +118,9 @@ class VideoDisplay : public wxGLCanvas { bool freeSize; /// @brief Draw an overscan mask - /// @param sizeH The amount of horizontal overscan on one side - /// @param sizeV The amount of vertical overscan on one side - /// @param colour The color of the mask - /// @param alpha The alpha of the mask - void DrawOverscanMask(int sizeH, int sizeV, wxColor color, double alpha) const; + /// @param horizontal_percent The percent of the video reserved horizontally + /// @param vertical_percent The percent of the video reserved vertically + void DrawOverscanMask(float horizontal_percent, float vertical_percent) const; /// Upload the image for the current frame to the video card void UploadFrameData(FrameReadyEvent&); @@ -144,21 +129,8 @@ class VideoDisplay : public wxGLCanvas { /// @return Could the context be set? bool InitContext(); - /// @brief Set this video display to the given frame - /// @frameNumber The desired frame number - void SetFrame(int frameNumber); - void OnVideoOpen(); - void OnCommit(int type); - void SetMode(int mode); - /// @brief Switch the active tool to a new object of the specified class - /// @param T The class of the new visual typesetting tool - template void SetTool(); - - /// @brief Set the cursor to either default or blank - /// @param show Whether or not the cursor should be visible - void ShowCursor(bool show); /// @brief Set the size of the display based on the current zoom and video resolution void UpdateSize(int arType = -1, double arValue = -1.); /// @brief Set the zoom level to that indicated by the dropdown @@ -169,11 +141,9 @@ class VideoDisplay : public wxGLCanvas { /// @brief Mouse event handler void OnMouseEvent(wxMouseEvent& event); void OnMouseWheel(wxMouseEvent& event); - void OnMouseEnter(wxMouseEvent& event); void OnMouseLeave(wxMouseEvent& event); /// @brief Recalculate video positioning and scaling when the available area or zoom changes void OnSizeEvent(wxSizeEvent &event); - void OnMode(const wxCommandEvent &event); void OnContextMenu(wxContextMenuEvent&); public: @@ -195,17 +165,8 @@ public: /// @brief Get the current zoom level double GetZoom() const { return zoomValue; } - /// @brief Convert a point from screen to script coordinate frame - /// @param x x coordinate; in/out - /// @param y y coordinate; in/out - void ToScriptCoords(int *x, int *y) const; - /// @brief Convert a point from script to screen coordinate frame - /// @param x x coordinate; in/out - /// @param y y coordinate; in/out - void FromScriptCoords(int *x, int *y) const; + /// Get the last seen position of the mouse in script coordinates + Vector2D GetMousePosition() const; - /// Get the last seen position of the mouse in screen coordinates - /// @param[out] x x coordinate - /// @param[out] y y coordinate - void GetMousePosition(int *x, int *y) const; + void SetTool(VisualToolBase *new_tool); }; diff --git a/aegisub/src/visual_feature.cpp b/aegisub/src/visual_feature.cpp index 68b61cfcc..4e5a7032d 100644 --- a/aegisub/src/visual_feature.cpp +++ b/aegisub/src/visual_feature.cpp @@ -40,69 +40,89 @@ #include "visual_feature.h" VisualDraggableFeature::VisualDraggableFeature() -: type(DRAG_NONE) -, x(INT_MIN) -, y(INT_MIN) -, origX(INT_MIN) -, origY(INT_MIN) +: start(Vector2D::Bad()) +, type(DRAG_NONE) +, pos(Vector2D::Bad()) , layer(0) -, line(NULL) +, line(0) { } -bool VisualDraggableFeature::IsMouseOver(int mx,int my) const { +bool VisualDraggableFeature::IsMouseOver(Vector2D mouse_pos) const { + if (!pos) return false; + + Vector2D delta = mouse_pos - pos; + switch (type) { case DRAG_BIG_SQUARE: - return !(mx < x-8 || mx > x+8 || my < y-8 || my > y+8); - case DRAG_BIG_CIRCLE: { - int dx = mx-x; - int dy = my-y; - return dx*dx + dy*dy <= 64; - } + return fabs(delta.X()) < 8 && fabs(delta.Y()) < 8; + + case DRAG_BIG_CIRCLE: + return delta.SquareLen() < 64; + case DRAG_BIG_TRIANGLE: { - int _my = my+2; - if (_my < y-8 || _my > y+8) return false; - int dx = mx-x; - int dy = _my-y-8; - return (16*dx+9*dy < 0 && 16*dx-9*dy > 0); + if (delta.Y() < -10 || delta.Y() > 6) return false; + float dy = delta.Y() - 6; + return 16 * delta.X() + 9 * dy < 0 && 16 * delta.X() - 9 * dy > 0; } + case DRAG_SMALL_SQUARE: - return !(mx < x-4 || mx > x+4 || my < y-4 || my > y+4); - case DRAG_SMALL_CIRCLE: { - int dx = mx-x; - int dy = my-y; - return dx*dx + dy*dy <= 16; - } + return fabs(delta.X()) < 4 && fabs(delta.Y()) < 4; + + case DRAG_SMALL_CIRCLE: + return delta.SquareLen() < 16; + default: return false; } } void VisualDraggableFeature::Draw(OpenGLWrapper const& gl) const { + if (!pos) return; + switch (type) { case DRAG_BIG_SQUARE: - gl.DrawRectangle(x-8,y-8,x+8,y+8); - gl.DrawLine(x,y-16,x,y+16); - gl.DrawLine(x-16,y,x+16,y); + gl.DrawRectangle(pos - 8, pos + 8); + gl.DrawLine(pos - Vector2D(0, 16), pos + Vector2D(0, 16)); + gl.DrawLine(pos - Vector2D(16, 0), pos + Vector2D(16, 0)); break; + case DRAG_BIG_CIRCLE: - gl.DrawCircle(x,y,8); - gl.DrawLine(x,y-16,x,y+16); - gl.DrawLine(x-16,y,x+16,y); + gl.DrawCircle(pos, 8); + gl.DrawLine(pos - Vector2D(0, 16), pos + Vector2D(0, 16)); + gl.DrawLine(pos - Vector2D(16, 0), pos + Vector2D(16, 0)); break; + case DRAG_BIG_TRIANGLE: - gl.DrawTriangle(x-9,y-6,x+9,y-6,x,y+10); - gl.DrawLine(x,y,x,y-16); - gl.DrawLine(x,y,x-14,y+8); - gl.DrawLine(x,y,x+14,y+8); + gl.DrawTriangle(pos - Vector2D(9, 6), pos + Vector2D(9, -6), pos + Vector2D(0, 10)); + gl.DrawLine(pos, pos + Vector2D(0, -16)); + gl.DrawLine(pos, pos + Vector2D(-14, 8)); + gl.DrawLine(pos, pos + Vector2D(14, 8)); break; + case DRAG_SMALL_SQUARE: - gl.DrawRectangle(x-4,y-4,x+4,y+4); + gl.DrawRectangle(pos - 4, pos + 4); break; + case DRAG_SMALL_CIRCLE: - gl.DrawCircle(x,y,4); + gl.DrawCircle(pos, 4); break; default: break; } } + +void VisualDraggableFeature::StartDrag() { + start = pos; +} + +void VisualDraggableFeature::UpdateDrag(Vector2D d, bool single_axis) { + if (single_axis) + d = d.SingleAxis(); + + pos = start + d; +} + +bool VisualDraggableFeature::HasMoved() const { + return pos != start; +} diff --git a/aegisub/src/visual_feature.h b/aegisub/src/visual_feature.h index ca1a857b6..82c49a21d 100644 --- a/aegisub/src/visual_feature.h +++ b/aegisub/src/visual_feature.h @@ -36,6 +36,8 @@ #pragma once +#include "vector2d.h" + class OpenGLWrapper; class AssDialogue; @@ -65,6 +67,8 @@ enum DraggableFeatureType { /// @class VisualDraggableFeature /// @brief Onscreen control used by many visual tools which doesn't do much class VisualDraggableFeature { + Vector2D start; ///< position before the last operation began + public: /// @brief Constructor VisualDraggableFeature(); @@ -72,21 +76,23 @@ public: /// Shape of feature DraggableFeatureType type; - int x; /// x coordinate - int y; /// y coordinate - - int origX; /// x coordindate before the last operation began - int origY; /// y coordindate before the last operation began + Vector2D pos; int layer; /// Layer; Higher = above AssDialogue* line; /// The dialogue line this feature is for /// @brief Is the given point over this feature? - /// @param mx x coordinate to test - /// @param my y coordinate to test - bool IsMouseOver(int x,int y) const; + /// @param mouse_pos Position of the mouse + bool IsMouseOver(Vector2D mouse_pos) const; + /// @brief Draw this feature /// @param gl OpenGLWrapper to use void Draw(OpenGLWrapper const& gl) const; + + void StartDrag(); + + void UpdateDrag(Vector2D d, bool single_axis); + + bool HasMoved() const; }; diff --git a/aegisub/src/visual_tool.cpp b/aegisub/src/visual_tool.cpp index d06666718..aa915f3bc 100644 --- a/aegisub/src/visual_tool.cpp +++ b/aegisub/src/visual_tool.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -40,6 +27,8 @@ #include #endif +#include "visual_tool.h" + #include "ass_dialogue.h" #include "ass_file.h" #include "ass_override.h" @@ -47,133 +36,219 @@ #include "ass_time.h" #include "include/aegisub/context.h" #include "main.h" -#include "subs_grid.h" #include "utils.h" #include "video_context.h" #include "video_display.h" #include "video_provider_manager.h" #include "visual_feature.h" -#include "visual_tool.h" #include "visual_tool_clip.h" #include "visual_tool_drag.h" #include "visual_tool_vector_clip.h" -const wxColour IVisualTool::colour[4] = {wxColour(106,32,19), wxColour(255,169,40), wxColour(255,253,185), wxColour(187,0,0)}; +template +static void for_each(C &range, F func) { + std::for_each(range.begin(), range.end(), func); +} -template -VisualTool::VisualTool(VideoDisplay *parent, agi::Context *context, VideoState const& video) -: dragStartX(0) -, dragStartY(0) -, commitId(-1) -, selChanged(false) -, selectedFeatures(selFeatures) -, c(context) +using std::tr1::placeholders::_1; + +const wxColour VisualToolBase::colour[4] = {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) , parent(parent) , holding(false) +, active_line(0) , dragging(false) -, externalChange(true) -, video(video) -, leftClick(false) -, leftDClick(false) -, shiftDown(false) -, ctrlDown(false) -, altDown(false) +, frame_number(c->videoController->GetFrameN()) +, left_click(false) +, left_double(false) +, shift_down(false) +, ctrl_down(false) +, alt_down(false) +, file_changed_connection(c->ass->AddCommitListener(&VisualToolBase::OnCommit, this)) +, commit_id(-1) { - frameNumber = c->videoController->GetFrameN(); - curDiag = GetActiveDialogueLine(); + int script_w, script_h; + c->ass->GetResolution(script_w, script_h); + script_res = Vector2D(script_w, script_h); + active_line = GetActiveDialogueLine(); c->selectionController->AddSelectionListener(this); - curFeature = features.begin(); + connections.push_back(c->videoController->AddSeekListener(&VisualToolBase::OnSeek, this)); + parent->Bind(wxEVT_MOUSE_CAPTURE_LOST, &VisualToolBase::OnMouseCaptureLost, this); +} + +VisualToolBase::~VisualToolBase() { + c->selectionController->RemoveSelectionListener(this); +} + +void VisualToolBase::OnCommit(int type) { + holding = false; + dragging = false; + + if (type & AssFile::COMMIT_NEW || type & AssFile::COMMIT_SCRIPTINFO) { + int script_w, script_h; + c->ass->GetResolution(script_w, script_h); + script_res = Vector2D(script_w, script_h); + OnCoordinateSystemsChanged(); + } + + if (type & AssFile::COMMIT_DIAG_FULL || type & AssFile::COMMIT_DIAG_ADDREM) { + active_line = GetActiveDialogueLine(); + OnFileChanged(); + } +} + +void VisualToolBase::OnSeek(int new_frame) { + if (frame_number == new_frame) return; + + frame_number = new_frame; + dragging = false; + OnFrameChanged(); + + AssDialogue *new_line = GetActiveDialogueLine(); + if (new_line != active_line) { + active_line = new_line; + OnLineChanged(); + } +} + +void VisualToolBase::OnMouseCaptureLost(wxMouseCaptureLostEvent &) { + holding = false; + dragging = false; + left_click = false; +} + +void VisualToolBase::OnActiveLineChanged(AssDialogue *new_line) { + if (!IsDisplayed(new_line)) + new_line = 0; + + holding = false; + dragging = false; + if (new_line != active_line) { + active_line = new_line; + OnLineChanged(); + } +} + +bool VisualToolBase::IsDisplayed(AssDialogue *line) const { + int frame = c->videoController->GetFrameN(); + return + line && + c->videoController->FrameAtTime(line->Start.GetMS(), agi::vfr::START) <= frame && + c->videoController->FrameAtTime(line->End.GetMS(), agi::vfr::END) >= frame; +} + +void VisualToolBase::Commit(wxString message) { + file_changed_connection.Block(); + if (message.empty()) + message = _("visual typesetting"); + + commit_id = c->ass->Commit(message, AssFile::COMMIT_DIAG_TEXT, commit_id); + file_changed_connection.Unblock(); +} + +AssDialogue* VisualToolBase::GetActiveDialogueLine() { + AssDialogue *diag = c->selectionController->GetActiveLine(); + if (IsDisplayed(diag)) + return diag; + return 0; +} + +void VisualToolBase::SetDisplayArea(int x, int y, int w, int h) { + video_pos = Vector2D(x, y); + video_res = Vector2D(w, h); + + holding = false; + dragging = false; + OnCoordinateSystemsChanged(); +} + +Vector2D VisualToolBase::ToScriptCoords(Vector2D point) const { + return (point - video_pos) * script_res / video_res; +} + +Vector2D VisualToolBase::FromScriptCoords(Vector2D point) const { + return (point * video_res / script_res) + video_pos; } template -VisualTool::~VisualTool() { - c->selectionController->RemoveSelectionListener(this); +VisualTool::VisualTool(VideoDisplay *parent, agi::Context *context) +: VisualToolBase(parent, context) +, sel_changed(false) +{ + active_feature = features.begin(); } template void VisualTool::OnMouseEvent(wxMouseEvent &event) { - bool needRender = false; + left_click = event.LeftDown(); + left_double = event.LeftDClick(); + shift_down = event.ShiftDown(); + ctrl_down = event.CmdDown(); + alt_down = event.AltDown(); + + mouse_pos = event.GetPosition(); + + bool need_render = false; if (event.Leaving()) { - Update(); + mouse_pos = Vector2D::Bad(); parent->Render(); return; } - else if (event.Entering() && !OPT_GET("Tool/Visual/Always Show")->GetBool()) { - needRender = true; - } - externalChange = false; - leftClick = event.ButtonDown(wxMOUSE_BTN_LEFT); - leftDClick = event.LeftDClick(); - shiftDown = event.m_shiftDown; -#ifdef __APPLE__ - ctrlDown = event.m_metaDown; // Cmd key -#else - ctrlDown = event.m_controlDown; -#endif - altDown = event.m_altDown; + if (event.Entering() && !OPT_GET("Tool/Visual/Always Show")->GetBool()) + need_render = true; if (!dragging) { - feature_iterator oldHigh = curFeature; - GetHighlightedFeature(); - if (curFeature != oldHigh) needRender = true; + 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; + } + } + + need_render |= active_feature != prev_feature; } if (dragging) { // continue drag if (event.LeftIsDown()) { - for (selection_iterator cur = selFeatures.begin(); cur != selFeatures.end(); ++cur) { - (*cur)->x = (video.x - dragStartX + (*cur)->origX); - (*cur)->y = (video.y - dragStartY + (*cur)->origY); - if (shiftDown) { - if (abs(video.x - dragStartX) > abs(video.y - dragStartY)) { - (*cur)->y = (*cur)->origY; - } - else { - (*cur)->x = (*cur)->origX; - } - } - UpdateDrag(*cur); - CommitDrag(*cur); - } + for_each(sel_features, bind(&FeatureType::UpdateDrag, _1, + mouse_pos - drag_start, shift_down)); + for_each(sel_features, bind(&VisualTool::UpdateDrag, this, _1)); Commit(); - needRender = true; + need_render = true; } // end drag else { dragging = false; // mouse didn't move, fiddle with selection - if (curFeature->x == curFeature->origX && curFeature->y == curFeature->origY) { + if (active_feature != features.end() && !active_feature->HasMoved()) { // Don't deselect stuff that was selected in this click's mousedown event - if (!selChanged) { - if (ctrlDown) { - // deselect this feature - RemoveSelection(curFeature); - } - else { - SetSelection(curFeature); - } + if (!sel_changed) { + if (ctrl_down) + RemoveSelection(active_feature); + else + SetSelection(active_feature, true); } } - else { - for (selection_iterator cur = selFeatures.begin(); cur != selFeatures.end(); ++cur) { - CommitDrag(*cur); - } - Commit(); - } - curFeature = features.end(); + active_feature = features.end(); parent->ReleaseMouse(); parent->SetFocus(); } } else if (holding) { - // continue hold if (event.LeftIsDown()) { UpdateHold(); - needRender = true; + need_render = true; } // end hold else { @@ -182,422 +257,318 @@ void VisualTool::OnMouseEvent(wxMouseEvent &event) { parent->ReleaseMouse(); parent->SetFocus(); } - CommitHold(); Commit(); } - else if (leftClick) { + else if (left_click) { + drag_start = mouse_pos; + // start drag - if (curFeature != features.end()) { - if (selFeatures.find(curFeature) == selFeatures.end()) { - selChanged = true; - if (ctrlDown) { - AddSelection(curFeature); - } - else { - SetSelection(curFeature); - } + if (active_feature != features.end()) { + if (!sel_features.count(active_feature)) { + sel_changed = true; + SetSelection(active_feature, !ctrl_down); } - else { - selChanged = false; - } - if (curFeature->line) c->selectionController->SetActiveLine(curFeature->line); + else + sel_changed = false; - if (InitializeDrag(curFeature)) { - dragStartX = video.x; - dragStartY = video.y; - for (selection_iterator cur = selFeatures.begin(); cur != selFeatures.end(); ++cur) { - (*cur)->origX = (*cur)->x; - (*cur)->origY = (*cur)->y; - } + if (active_feature->line) + c->selectionController->SetActiveLine(active_feature->line); + if (InitializeDrag(active_feature)) { + for_each(sel_features, bind(&VisualDraggableFeature::StartDrag, _1)); dragging = true; parent->CaptureMouse(); } } // start hold else { - if (!altDown) { - ClearSelection(); + if (!alt_down) { + sel_features.clear(); Selection sel; sel.insert(c->selectionController->GetActiveLine()); c->selectionController->SetSelectedSet(sel); - needRender = true; + need_render = true; } - if (curDiag && InitializeHold()) { + if (active_line && InitializeHold()) { holding = true; parent->CaptureMouse(); } } } - if (Update() || needRender) parent->Render(); - externalChange = true; + if (active_line && left_double) + OnDoubleClick(); - if (!event.LeftIsDown()) { - // Only coalesce the changes made in a single drag - commitId = -1; - } -} + //if (need_render) + parent->Render(); -template -void VisualTool::Commit(wxString message) { - externalChange = false; - if (message.empty()) { - message = _("visual typesetting"); - } - commitId = c->ass->Commit(message, AssFile::COMMIT_DIAG_TEXT, commitId); - externalChange = true; -} - -template -AssDialogue* VisualTool::GetActiveDialogueLine() { - AssDialogue *diag = c->selectionController->GetActiveLine(); - if (diag && c->subsGrid->IsDisplayed(diag)) - return diag; - return NULL; -} - -template -void VisualTool::GetHighlightedFeature() { - int highestLayerFound = INT_MIN; - curFeature = features.end(); - for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) { - if (cur->IsMouseOver(video.x, video.y) && cur->layer > highestLayerFound) { - curFeature = cur; - highestLayerFound = cur->layer; - } - } + // Only coalesce the changes made in a single drag + if (!event.LeftIsDown()) + commit_id = -1; } template void VisualTool::DrawAllFeatures() { - SetLineColour(colour[0],1.0f,2); + gl.SetLineColour(colour[0], 1.0f, 2); for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) { - int fill; - if (cur == curFeature) + int fill = 1; + if (cur == active_feature) fill = 2; - else if (selFeatures.find(cur) != selFeatures.end()) + else if (sel_features.count(cur)) fill = 3; - else - fill = 1; - SetFillColour(colour[fill],0.6f); - cur->Draw(*this); + gl.SetFillColour(colour[fill], 0.6f); + cur->Draw(gl); } } template -void VisualTool::Refresh() { - if (externalChange) { - curDiag = GetActiveDialogueLine(); - curFeature = features.end(); - OnFileChanged(); - } -} - -template -void VisualTool::SetFrame(int newFrameNumber) { - if (frameNumber == newFrameNumber) return; - frameNumber = newFrameNumber; - curFeature = features.end(); - OnFrameChanged(); - AssDialogue *newCurDiag = GetActiveDialogueLine(); - if (newCurDiag != curDiag) { - curDiag = newCurDiag; - OnLineChanged(); - } -} - -template -void VisualTool::OnActiveLineChanged(AssDialogue *new_line) { - if (new_line && !c->subsGrid->IsDisplayed(new_line)) { - new_line = NULL; - } - if (new_line != curDiag) { - curDiag = new_line; - OnLineChanged(); - } -} - - -template -void VisualTool::SetSelection(feature_iterator feat) { - selFeatures.clear(); - lineSelCount.clear(); - - selFeatures.insert(feat); - - AssDialogue *line = feat->line; - if (line) { - lineSelCount[line] = 1; +void VisualTool::SetSelection(feature_iterator feat, bool clear) { + if (clear) + sel_features.clear(); + if (sel_features.insert(feat).second && feat->line) { Selection sel; - sel.insert(line); - c->selectionController->SetSelectedSet(sel); - } -} - - -template -void VisualTool::AddSelection(feature_iterator feat) { - if (selFeatures.insert(feat).second && feat->line) { - lineSelCount[feat->line] += 1; - Selection sel = c->selectionController->GetSelectedSet(); - if (sel.insert(feat->line).second) { + if (!clear) + sel = c->selectionController->GetSelectedSet(); + if (sel.insert(feat->line).second) c->selectionController->SetSelectedSet(sel); - } } } template void VisualTool::RemoveSelection(feature_iterator feat) { - if (selFeatures.erase(feat) > 0 && feat->line) { - // Deselect a line only if all features for that line have been - // deselected - AssDialogue* line = feat->line; - lineSelCount[line] -= 1; - assert(lineSelCount[line] >= 0); - if (lineSelCount[line] <= 0) { - Selection sel = c->selectionController->GetSelectedSet(); + if (!sel_features.erase(feat) || !feat->line) return; - // Don't deselect the only selected line - if (sel.size() <= 1) return; - - sel.erase(line); - - // Set the active line to an arbitrary selected line if we just - // deselected the active line - if (line == c->selectionController->GetActiveLine()) { - c->selectionController->SetActiveLine(*sel.begin()); - } - - c->selectionController->SetSelectedSet(sel); - } + for (selection_iterator it = sel_features.begin(); it != sel_features.end(); ++it) { + if ((*it)->line == feat->line) return; } + + Selection sel = c->selectionController->GetSelectedSet(); + + // Don't deselect the only selected line + if (sel.size() <= 1) return; + + sel.erase(feat->line); + + // Set the active line to an arbitrary selected line if we just + // deselected the active line + if (feat->line == c->selectionController->GetActiveLine()) { + c->selectionController->SetActiveLine(*sel.begin()); + } + + c->selectionController->SetSelectedSet(sel); } -template -void VisualTool::ClearSelection() { - selFeatures.clear(); - lineSelCount.clear(); -} +//////// PARSERS -enum TagFoundType { - TAG_NOT_FOUND = 0, - PRIMARY_TAG_FOUND, - ALT_TAG_FOUND +typedef const std::vector * param_vec; + +// Parse line on creation and unparse at the end of scope +struct scoped_tag_parse { + AssDialogue *diag; + scoped_tag_parse(AssDialogue *diag) : diag(diag) { diag->ParseASSTags(); } + ~scoped_tag_parse() { diag->ClearBlocks(); } }; -/// @brief Get the first value set for a tag -/// @param line Line to get the value from -/// @param tag Tag to get the value of -/// @param n Number of parameters passed -/// @return Which tag (if any) was found -template -static TagFoundType get_value(const AssDialogue *line, wxString tag, size_t n, ...) { - wxString alt; - if (tag == "\\pos") alt = "\\move"; - else if (tag == "\\an") alt = "\\a"; - else if (tag == "\\clip") alt = "\\iclip"; - - for (size_t i = 0; i < line->Blocks.size(); i++) { +// Find a tag's parameters in a line or return NULL if it's not found +static param_vec find_tag(const AssDialogue *line, wxString tag_name) { + for (size_t i = 0; i < line->Blocks.size(); ++i) { const AssDialogueBlockOverride *ovr = dynamic_cast(line->Blocks[i]); if (!ovr) continue; - for (size_t j=0; j < ovr->Tags.size(); j++) { - const AssOverrideTag *cur = ovr->Tags[j]; - if ((cur->Name == tag || cur->Name == alt) && cur->Params.size() >= n) { - va_list argp; - va_start(argp, n); - for (size_t j = 0; j < n; j++) { - T *val = va_arg(argp, T *); - *val = cur->Params[j]->Get(*val); - } - va_end(argp); - return cur->Name == alt ? ALT_TAG_FOUND : PRIMARY_TAG_FOUND; - } + for (size_t j = 0; j < ovr->Tags.size(); ++j) { + if (ovr->Tags[j]->Name == tag_name) + return &ovr->Tags[j]->Params; } } - return TAG_NOT_FOUND; + + return 0; } -template -void VisualTool::GetLinePosition(AssDialogue *diag,int &x, int &y) { - int orgx,orgy; - GetLinePosition(diag,x,y,orgx,orgy); +// Get a Vector2D from the given tag parameters, or Vector2D::Bad() if they are not valid +static Vector2D vec_or_bad(param_vec tag, size_t x_idx, size_t y_idx) { + if (!tag || + tag->size() <= x_idx || tag->size() <= y_idx || + (*tag)[x_idx]->omitted || (*tag)[y_idx]->omitted || + (*tag)[x_idx]->GetType() == VARDATA_NONE || (*tag)[y_idx]->GetType() == VARDATA_NONE) + { + return Vector2D::Bad(); + } + return Vector2D((*tag)[x_idx]->Get(), (*tag)[y_idx]->Get()); } -template -void VisualTool::GetLinePosition(AssDialogue *diag,int &x, int &y, int &orgx, int &orgy) { +Vector2D VisualToolBase::GetLinePosition(AssDialogue *diag) { + scoped_tag_parse parse(diag); + + if (Vector2D ret = vec_or_bad(find_tag(diag, "\\pos"), 0, 1)) return ret; + if (Vector2D ret = vec_or_bad(find_tag(diag, "\\move"), 0, 1)) return ret; + + // Get default position int margin[4]; - for (int i=0;i<4;i++) margin[i] = diag->Margin[i]; + std::copy(diag->Margin, diag->Margin + 4, margin); int align = 2; - AssStyle *style = c->ass->GetStyle(diag->Style); - if (style) { + if (AssStyle *style = c->ass->GetStyle(diag->Style)) { align = style->alignment; - for (int i=0;i<4;i++) { - if (margin[i] == 0) margin[i] = style->Margin[i]; + for (int i = 0; i < 4; i++) { + if (margin[i] == 0) + margin[i] = style->Margin[i]; } } - int sw,sh; - c->videoController->GetScriptSize(sw,sh); + param_vec align_tag; + if ((align_tag = find_tag(diag, "\\an")) && !(*align_tag)[0]->omitted) + align = (*align_tag)[0]->Get(); + else if ((align_tag = find_tag(diag, "\\a"))) { + align = (*align_tag)[0]->Get(2); - // Process margins - margin[1] = sw - margin[1]; - margin[3] = sh - margin[2]; - - // Overrides processing - diag->ParseASSTags(); - - if (!get_value(diag, "\\pos", 2, &x, &y)) { - if (get_value(diag, "\\an", 1, &align) == ALT_TAG_FOUND) { - switch(align) { - case 1: case 2: case 3: - break; - case 5: case 6: case 7: - align += 2; - break; - case 9: case 10: case 11: - align -= 5; - break; - default: - align = 2; - break; - } - } - // Alignment type - int hor = (align - 1) % 3; - int vert = (align - 1) / 3; - - // Calculate positions - if (hor == 0) x = margin[0]; - else if (hor == 1) x = (margin[0] + margin[1])/2; - else if (hor == 2) x = margin[1]; - if (vert == 0) y = margin[3]; - else if (vert == 1) y = (margin[2] + margin[3])/2; - else if (vert == 2) y = margin[2]; + // \a -> \an values mapping + static int align_mapping[] = { 2, 1, 2, 3, 7, 7, 8, 9, 7, 4, 5, 6 }; + if (static_cast(align) < sizeof(align_mapping) / sizeof(int)) + align = align_mapping[align]; + else + align = 2; } - parent->FromScriptCoords(&x, &y); + // Alignment type + int hor = (align - 1) % 3; + int vert = (align - 1) / 3; - if (!get_value(diag, "\\org", 2, &orgx, &orgy)) { - orgx = x; - orgy = y; - } - else { - parent->FromScriptCoords(&orgx, &orgy); - } + // Calculate positions + int x, y; + if (hor == 0) + x = margin[0]; + else if (hor == 1) + x = (script_res.X() + margin[0] - margin[1]) / 2; + else if (hor == 2) + x = margin[1]; - diag->ClearBlocks(); + if (vert == 0) + y = script_res.Y() - margin[2]; + else if (vert == 1) + y = script_res.Y() / 2; + else if (vert == 2) + y = margin[2]; + + return Vector2D(x, y); } -template -void VisualTool::GetLineMove(AssDialogue *diag,bool &hasMove,int &x1,int &y1,int &x2,int &y2,int &t1,int &t2) { - diag->ParseASSTags(); - - hasMove = - get_value(diag, "\\move", 6, &x1, &y1, &x2, &y2, &t1, &t2) || - get_value(diag, "\\move", 4, &x1, &y1, &x2, &y2); - - if (hasMove) { - parent->FromScriptCoords(&x1, &y1); - parent->FromScriptCoords(&x2, &y2); - } - - diag->ClearBlocks(); +Vector2D VisualToolBase::GetLineOrigin(AssDialogue *diag) { + scoped_tag_parse parse(diag); + return vec_or_bad(find_tag(diag, "\\org"), 0, 1); } -template -void VisualTool::GetLineRotation(AssDialogue *diag,float &rx,float &ry,float &rz) { +bool VisualToolBase::GetLineMove(AssDialogue *diag, Vector2D &p1, Vector2D &p2, int &t1, int &t2) { + scoped_tag_parse parse(diag); + + param_vec tag = find_tag(diag, "\\move"); + if (!tag) + return false; + + p1 = vec_or_bad(tag, 0, 1); + p2 = vec_or_bad(tag, 2, 3); + // VSFilter actually defaults to -1, but it uses <= 0 to check for default and 0 seems less bug-prone + t1 = (*tag)[4]->Get(0); + t2 = (*tag)[5]->Get(0); + + return p1 && p2; +} + +void VisualToolBase::GetLineRotation(AssDialogue *diag, float &rx, float &ry, float &rz) { rx = ry = rz = 0.f; - AssStyle *style = c->ass->GetStyle(diag->Style); - if (style) { + if (AssStyle *style = c->ass->GetStyle(diag->Style)) rz = style->angle; - } - diag->ParseASSTags(); + scoped_tag_parse parse(diag); - get_value(diag, "\\frx", 1, &rx); - get_value(diag, "\\fry", 1, &ry); - get_value(diag, "\\frz", 1, &rz); - - diag->ClearBlocks(); + if (param_vec tag = find_tag(diag, "\\frx")) + rx = tag->front()->Get(rx); + if (param_vec tag = find_tag(diag, "\\fry")) + ry = tag->front()->Get(ry); + if (param_vec tag = find_tag(diag, "\\frz")) + rz = tag->front()->Get(rz); + else if (param_vec tag = find_tag(diag, "\\fr")) + rz = tag->front()->Get(rz); } -template -void VisualTool::GetLineScale(AssDialogue *diag,float &scalX,float &scalY) { - scalX = scalY = 100.f; +void VisualToolBase::GetLineScale(AssDialogue *diag, Vector2D &scale) { + float x = 100.f, y = 100.f; - AssStyle *style = c->ass->GetStyle(diag->Style); - if (style) { - scalX = style->scalex; - scalY = style->scaley; + if (AssStyle *style = c->ass->GetStyle(diag->Style)) { + x = style->scalex; + y = style->scaley; } - diag->ParseASSTags(); + scoped_tag_parse parse(diag); - get_value(diag, "\\fscx", 1, &scalX); - get_value(diag, "\\fscy", 1, &scalY); + if (param_vec tag = find_tag(diag, "\\fscx")) + x = tag->front()->Get(x); + if (param_vec tag = find_tag(diag, "\\fscy")) + y = tag->front()->Get(y); - diag->ClearBlocks(); + scale = Vector2D(x, y); } -template -void VisualTool::GetLineClip(AssDialogue *diag,int &x1,int &y1,int &x2,int &y2,bool &inverse) { - x1 = y1 = 0; - int sw,sh; - c->videoController->GetScriptSize(sw,sh); - x2 = sw-1; - y2 = sh-1; +void VisualToolBase::GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse) { inverse = false; - diag->ParseASSTags(); - inverse = get_value(diag, "\\clip", 4, &x1, &y1, &x2, &y2) == ALT_TAG_FOUND; - diag->ClearBlocks(); + scoped_tag_parse parse(diag); + param_vec tag = find_tag(diag, "\\iclip"); + if (tag) + inverse = true; + else + tag = find_tag(diag, "\\clip"); - parent->FromScriptCoords(&x1, &y1); - parent->FromScriptCoords(&x2, &y2); + if (tag && tag->size() == 4) { + p1 = vec_or_bad(tag, 0, 1); + p2 = vec_or_bad(tag, 2, 3); + } + else { + p1 = Vector2D(); + p2 = script_res - 1; + } } -template -wxString VisualTool::GetLineVectorClip(AssDialogue *diag,int &scale,bool &inverse) { +wxString VisualToolBase::GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse) { + scoped_tag_parse parse(diag); + scale = 1; inverse = false; - diag->ParseASSTags(); - int x1, y1, x2, y2; - TagFoundType res = get_value(diag, "\\clip", 4, &x1, &y1, &x2, &y2); - if (res) { - inverse = res == ALT_TAG_FOUND; - diag->ClearBlocks(); - return wxString::Format("m %d %d l %d %d %d %d %d %d", x1, y1, x2, y1, x2, y2, x1, y2); + param_vec tag = find_tag(diag, "\\iclip"); + if (tag) + inverse = true; + else + tag = find_tag(diag, "\\clip"); + + if (tag && tag->size() == 4) { + return wxString::Format("m %d %d l %d %d %d %d %d %d", + (*tag)[0]->Get(), (*tag)[1]->Get(), + (*tag)[2]->Get(), (*tag)[1]->Get(), + (*tag)[2]->Get(), (*tag)[3]->Get(), + (*tag)[0]->Get(), (*tag)[3]->Get()); } - wxString result; - wxString scaleStr; - res = get_value(diag, "\\clip", 2, &scaleStr, &result); - inverse = res == ALT_TAG_FOUND; - if (!scaleStr.empty()) { - long s; - scaleStr.ToLong(&s); - scale = s; + if (tag) { + scale = (*tag)[0]->Get(scale); + return (*tag)[1]->Get(""); } - diag->ClearBlocks(); - return result; + + return ""; } -/// @brief Set override -/// @param tag -/// @param value -template -void VisualTool::SetOverride(AssDialogue* line, wxString tag, wxString value) { +void VisualToolBase::SetSelectedOverride(wxString const& tag, wxString const& value) { + for_each(c->selectionController->GetSelectedSet(), + bind(&VisualToolBase::SetOverride, this, _1, tag, value)); +} + +void VisualToolBase::SetOverride(AssDialogue* line, wxString const& tag, wxString const& value) { if (!line) return; wxString removeTag; if (tag == "\\1c") removeTag = "\\c"; - else if (tag == "\\fr") removeTag = "\\frz"; + else if (tag == "\\frz") removeTag = "\\fr"; else if (tag == "\\pos") removeTag = "\\move"; else if (tag == "\\move") removeTag = "\\pos"; else if (tag == "\\clip") removeTag = "\\iclip"; @@ -607,17 +578,14 @@ void VisualTool::SetOverride(AssDialogue* line, wxString tag, wxStr // Get block at start line->ParseASSTags(); - AssDialogueBlock *block = line->Blocks.at(0); + AssDialogueBlock *block = line->Blocks.front(); // Get current block as plain or override - AssDialogueBlockPlain *plain = dynamic_cast(block); - AssDialogueBlockOverride *ovr = dynamic_cast(block); assert(dynamic_cast(block) == NULL); - if (plain) { + if (dynamic_cast(block)) line->Text = "{" + insert + "}" + line->Text; - } - else if (ovr) { + else if (AssDialogueBlockOverride *ovr = dynamic_cast(block)) { // Remove old of same for (size_t i = 0; i < ovr->Tags.size(); i++) { wxString name = ovr->Tags[i]->Name; @@ -631,8 +599,6 @@ void VisualTool::SetOverride(AssDialogue* line, wxString tag, wxStr line->UpdateText(); } - - parent->SetFocus(); } // If only export worked diff --git a/aegisub/src/visual_tool.h b/aegisub/src/visual_tool.h index 7f7d6fed0..1b305150a 100644 --- a/aegisub/src/visual_tool.h +++ b/aegisub/src/visual_tool.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -36,6 +23,7 @@ #pragma once #ifndef AGI_PRE +#include #include #include #include @@ -45,83 +33,158 @@ #include #endif -#include "base_grid.h" +#include + #include "gl_wrap.h" +#include "selection_controller.h" +#include "vector2d.h" class AssDialogue; class SubtitlesGrid; class VideoDisplay; -struct VideoState; +class wxToolBar; namespace agi { struct Context; class OptionValue; } -/// First window id for visualsubtoolbar items -#define VISUAL_SUB_TOOL_START 1300 +/// @class VisualToolBase +/// @brief Base class for visual tools containing all functionality that doesn't interact with features +/// +/// This is required so that visual tools can be used polymorphically, as +/// different VisualTools are unrelated types otherwise. In addition, as much +/// functionality as possible is implemented here to avoid having four copies +/// of each method for no good reason (and four times as many error messages) +class VisualToolBase : protected SelectionListener { + std::deque connections; -/// Last window id for visualsubtoolbar items -#define VISUAL_SUB_TOOL_END (VISUAL_SUB_TOOL_START+100) + void OnCommit(int type); + void OnSeek(int new_frame); -class IVisualTool : public OpenGLWrapper { -protected: - /// DOCME - static const wxColour colour[4]; -public: - virtual void OnMouseEvent(wxMouseEvent &event)=0; - virtual void OnSubTool(wxCommandEvent &)=0; - virtual bool Update()=0; - virtual void Draw()=0; - virtual void Refresh()=0; - virtual void SetFrame(int frame)=0; - virtual ~IVisualTool() { }; -}; - -struct ltaddr { - template - bool operator()(T lft, T rgt) const { - return &*lft < &*rgt; - } -}; - -/// DOCME -/// @class VisualTool -/// @brief DOCME -/// DOCME -template -class VisualTool : public IVisualTool, protected SubtitleSelectionListener { -protected: - typedef FeatureType Feature; - typedef typename std::list::iterator feature_iterator; - typedef typename std::list::const_iterator feature_const_iterator; -private: - int dragStartX; /// Starting x coordinate of the current drag, if any - int dragStartY; /// Starting y coordinate of the current drag, if any - - int commitId; - - /// Set curFeature to the topmost feature under the mouse, or end() if there - /// are none - void GetHighlightedFeature(); + void OnMouseCaptureLost(wxMouseCaptureLostEvent &); /// @brief Get the dialogue line currently in the edit box /// @return NULL if the line is not active on the current frame AssDialogue *GetActiveDialogueLine(); + // SubtitleSelectionListener implementation + void OnActiveLineChanged(AssDialogue *new_line); + void OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed) { } + + // Below here are the virtuals that must be implemented + + /// Called when the script, video or screen resolutions change + virtual void OnCoordinateSystemsChanged() { DoRefresh(); } + + /// Called when the file is changed by something other than a visual tool + virtual void OnFileChanged() { DoRefresh(); } + + /// Called when the frame number changes + virtual void OnFrameChanged() { } + + /// Called when the active line changes + virtual void OnLineChanged() { DoRefresh(); } + + /// Generic refresh to simplify tools which have no interesting state and + /// can simply do do the same thing for any external change (i.e. most of + /// them). Called only by the above virtual methods. + virtual void DoRefresh() { } + +protected: + OpenGLWrapper gl; + + /// Called when the user double-clicks + virtual void OnDoubleClick() { } + + static const wxColour colour[4]; + + agi::Context *c; + VideoDisplay *parent; + + bool holding; ///< Is a hold currently in progress? + AssDialogue *active_line; ///< Active dialogue line; NULL if it is not visible on the current frame + bool dragging; ///< Is a drag currently in progress? + + int frame_number; ///< Current frame number + + bool left_click; ///< Is a left click event currently being processed? + bool left_double; ///< Is a left double click event currently being processed? + bool shift_down; ///< Is shift down? + bool ctrl_down; ///< Is ctrl down? + bool alt_down; ///< Is alt down? + + Vector2D mouse_pos; ///< Last seen mouse position + Vector2D drag_start; ///< Mouse position at the beginning of the last drag + Vector2D script_res; ///< Script resolution + Vector2D video_pos; ///< Top-left corner of the video in the display area + Vector2D video_res; ///< Video resolution + + agi::signal::Connection file_changed_connection; + int commit_id; ///< Last used commit id for coalescing + + /// @brief Commit the current file state + /// @param message Description of changes for undo + void Commit(wxString message = ""); + bool IsDisplayed(AssDialogue *line) const; + + /// Get the line's position if it's set, or it's default based on style if not + Vector2D GetLinePosition(AssDialogue *diag); + /// Get the line's origin if it's set, or Vector2D::Bad() if not + Vector2D GetLineOrigin(AssDialogue *diag); + bool GetLineMove(AssDialogue *diag, Vector2D &p1, Vector2D &p2, int &t1, int &t2); + void GetLineRotation(AssDialogue *diag, float &rx, float &ry, float &rz); + void GetLineScale(AssDialogue *diag, Vector2D &scale); + void GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse); + wxString GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse); + + void SetOverride(AssDialogue* line, wxString const& tag, wxString const& value); + void SetSelectedOverride(wxString const& tag, wxString const& value); + + VisualToolBase(VideoDisplay *parent, agi::Context *context); + +public: + /// Convert a point from video to script coordinates + Vector2D ToScriptCoords(Vector2D point) const; + /// Convert a point from script to video coordinates + Vector2D FromScriptCoords(Vector2D point) const; + + // Stuff called by VideoDisplay + virtual void OnMouseEvent(wxMouseEvent &event)=0; + virtual void Draw()=0; + virtual void SetDisplayArea(int x, int y, int w, int h); + virtual void SetToolbar(wxToolBar *tb) { } + virtual ~VisualToolBase(); +}; + +/// @class VisualTool +/// @brief Visual tool base class containing all common feature-related functionality +/// DOCME +template +class VisualTool : public VisualToolBase { +protected: + typedef FeatureType Feature; + typedef typename std::list::iterator feature_iterator; + typedef typename std::list::const_iterator feature_const_iterator; + +private: + struct ltaddr { + template + bool operator()(T lft, T rgt) const { + return &*lft < &*rgt; + } + }; + + std::list slots; + typedef typename std::set::iterator selection_iterator; - std::set selFeatures; /// Currently selected visual features - std::map lineSelCount; /// Number of selected features for each line - - bool selChanged; /// 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 /// @return Should the hold actually happen? virtual bool InitializeHold() { return false; } /// @brief Called on every mouse event during a hold virtual void UpdateHold() { } - /// @brief Called at the end of a hold - virtual void CommitHold() { } /// @brief Called at the beginning of a drag /// @param feature The visual feature clicked on @@ -130,67 +193,22 @@ private: /// @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) { } - /// @brief Called at the end of a drag - virtual void CommitDrag(feature_iterator feature) { } - /// Called when the file is changed by something other than a visual tool - virtual void OnFileChanged() { DoRefresh(); } - /// Called when the frame number changes - virtual void OnFrameChanged() { } - /// Called when curDiag changes - virtual void OnLineChanged() { DoRefresh(); } - /// Generic refresh to simplify tools which do the same thing for any - /// external change (i.e. almost all of them). Called only by the above - /// methods. - virtual void DoRefresh() { } - - /// @brief Called when there's stuff - /// @return Should the display rerender? - virtual bool Update() { return false; }; /// @brief Draw stuff virtual void Draw()=0; protected: - /// Read-only reference to the set of selected features for subclasses - const std::set &selectedFeatures; + std::set sel_features; ///< Currently selected visual features typedef typename std::set::const_iterator sel_iterator; - agi::Context *c; - VideoDisplay *parent; /// VideoDisplay which this belongs to, used to frame conversion - bool holding; /// Is a hold currently in progress? - AssDialogue *curDiag; /// Active dialogue line; NULL if it is not visible on the current frame - bool dragging; /// Is a drag currently in progress? - bool externalChange; /// Only invalid drag lists when refreshing due to external changes - feature_iterator curFeature; /// Topmost feature under the mouse; generally only valid during a drag - std::list features; /// List of features which are drawn and can be clicked on - - int frameNumber; /// Current frame number - VideoState const& video; /// Mouse and video information - - bool leftClick; /// Is a left click event currently being processed? - bool leftDClick; /// Is a left double click event currently being processed? - bool shiftDown; /// Is shift down? - bool ctrlDown; /// Is ctrl down? - bool altDown; /// Is alt down? - - void GetLinePosition(AssDialogue *diag,int &x,int &y); - void GetLinePosition(AssDialogue *diag,int &x,int &y,int &orgx,int &orgy); - void GetLineMove(AssDialogue *diag,bool &hasMove,int &x1,int &y1,int &x2,int &y2,int &t1,int &t2); - 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,bool &inverse); - wxString GetLineVectorClip(AssDialogue *diag,int &scale,bool &inverse); - void SetOverride(AssDialogue* line, wxString tag, wxString value); + /// Topmost feature under the mouse; generally only valid during a drag + feature_iterator active_feature; + /// List of features which are drawn and can be clicked on + /// List is used here for the iterator invalidation properties + std::list features; /// Draw all of the features in the list void DrawAllFeatures(); - /// @brief Commit the current file state - /// @param message Description of changes for undo - void Commit(wxString message = ""); - - /// @brief Add a feature (and its line) to the selection - /// @param i Index in the feature list - void AddSelection(feature_iterator feat); /// @brief Remove a feature from the selection /// @param i Index in the feature list @@ -199,35 +217,15 @@ protected: /// @brief Set the selection to a single feature, deselecting everything else /// @param i Index in the feature list - void SetSelection(feature_iterator feat); - - /// @brief Clear the selection - void ClearSelection(); - - // SubtitleSelectionListener implementation - void OnActiveLineChanged(AssDialogue *new_line); - virtual void OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed) { } + void SetSelection(feature_iterator feat, bool clear); public: /// @brief Handler for all mouse events /// @param event Shockingly enough, the mouse event void OnMouseEvent(wxMouseEvent &event); - /// @brief Event handler for the subtoolbar - virtual void OnSubTool(wxCommandEvent &) { } - - /// @brief Signal that the file has changed - void Refresh(); - /// @brief Signal that the current frame number has changed - /// @param newFrameNumber The new frame number - void SetFrame(int newFrameNumber); - - /// @brief Constructor /// @param parent The VideoDisplay to use for coordinate conversion /// @param video Video and mouse information passing blob - VisualTool(VideoDisplay *parent, agi::Context *context, VideoState const& video); - - /// @brief Destructor - virtual ~VisualTool(); + VisualTool(VideoDisplay *parent, agi::Context *context); }; diff --git a/aegisub/src/visual_tool_clip.cpp b/aegisub/src/visual_tool_clip.cpp index c345c03a5..eefc60f61 100644 --- a/aegisub/src/visual_tool_clip.cpp +++ b/aegisub/src/visual_tool_clip.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -39,35 +26,30 @@ #include #endif -#include "ass_dialogue.h" -#include "ass_file.h" -#include "utils.h" -#include "video_display.h" #include "visual_tool_clip.h" -VisualToolClip::VisualToolClip(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *) -: VisualTool(parent, context, video) -, curX1(0) -, curY1(0) -, curX2(video.w) -, curY2(video.h) +#include "utils.h" + +VisualToolClip::VisualToolClip(VideoDisplay *parent, agi::Context *context) +: VisualTool(parent, context) +, cur_1(0, 0) +, cur_2(video_res) , inverse(false) { - if (curDiag) { - GetLineClip(curDiag,curX1,curY1,curX2,curY2,inverse); - } - 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++); - + // Attach each feature to the two features it shares edges with // Top-left int i = 0; feats[i]->horiz = feats[1]; @@ -87,129 +69,81 @@ VisualToolClip::VisualToolClip(VideoDisplay *parent, agi::Context *context, Vide // Bottom-right feats[i]->horiz = feats[2]; feats[i]->vert = feats[1]; - i++; } void VisualToolClip::Draw() { - if (!curDiag) return; + if (!active_line) return; - int dx1 = curX1; - int dy1 = curY1; - int dx2 = curX2; - int dy2 = curY2; + DrawAllFeatures(); // Draw rectangle - SetLineColour(colour[3],1.0f,2); - SetFillColour(colour[3],0.0f); - DrawRectangle(dx1,dy1,dx2,dy2); + gl.SetLineColour(colour[3], 1.0f, 2); + gl.SetFillColour(colour[3], 0.0f); + gl.DrawRectangle(cur_1, cur_2); // Draw outside area - SetLineColour(colour[3],0.0f); - SetFillColour(wxColour(0,0,0),0.5f); + gl.SetLineColour(colour[3], 0.0f); + gl.SetFillColour(*wxBLACK, 0.5f); if (inverse) { - DrawRectangle(dx1,dy1,dx2,dy2); + gl.DrawRectangle(cur_1, cur_2); } else { - DrawRectangle(0,0,video.w,dy1); - DrawRectangle(0,dy2,video.w,video.h); - DrawRectangle(0,dy1,dx1,dy2); - DrawRectangle(dx2,dy1,video.w,dy2); + Vector2D p1 = cur_1.Min(cur_2); + Vector2D p2 = cur_1.Max(cur_2); + gl.DrawRectangle(Vector2D(0, 0), Vector2D(video_res, p1)); + gl.DrawRectangle(Vector2D(0, p2), video_res); + gl.DrawRectangle(Vector2D(0, p1), Vector2D(p1, p2)); + gl.DrawRectangle(Vector2D(p2, p1), Vector2D(video_res, p2)); } - - // Draw circles - SetLineColour(colour[0]); - SetFillColour(colour[1],0.5); - DrawAllFeatures(); } bool VisualToolClip::InitializeHold() { - startX = video.x; - startY = video.y; - curDiag->StripTag("\\clip"); - curDiag->StripTag("\\iclip"); return true; } void VisualToolClip::UpdateHold() { - curX1 = startX; - curY1 = startY; - curX2 = video.x; - curY2 = video.y; - - // Make sure 1 is smaller than 2 - if (curX1 > curX2) std::swap(curX1,curX2); - if (curY1 > curY2) std::swap(curY1,curY2); - // Limit to video area - curX1 = mid(0,curX1,video.w); - curX2 = mid(0,curX2,video.w); - curY1 = mid(0,curY1,video.h); - curY2 = mid(0,curY2,video.h); + Vector2D zero(0, 0); + cur_1 = zero.Max(video_res.Min(drag_start)); + cur_2 = zero.Max(video_res.Min(mouse_pos)); SetFeaturePositions(); + CommitHold(); } void VisualToolClip::CommitHold() { - int x1 = curX1; - int x2 = curX2; - int y1 = curY1; - int y2 = curY2; - parent->ToScriptCoords(&x1, &y1); - parent->ToScriptCoords(&x2, &y2); - SetOverride(curDiag, inverse ? "\\iclip" : "\\clip",wxString::Format("(%i,%i,%i,%i)",x1,y1,x2,y2)); + SetOverride(active_line, inverse ? "\\iclip" : "\\clip", + wxString::Format("(%s,%s)", ToScriptCoords(cur_1.Min(cur_2)).Str(), ToScriptCoords(cur_1.Max(cur_2)).Str())); } bool VisualToolClip::InitializeDrag(feature_iterator) { - curDiag->StripTag("\\clip"); - curDiag->StripTag("\\iclip"); return true; } void VisualToolClip::UpdateDrag(feature_iterator feature) { - // Update brothers - feature->horiz->y = feature->y; - feature->vert->x = feature->x; + // 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); - // Get "cur" from features - curX1 = feats[0]->x; - curX2 = feats[3]->x; - curY1 = feats[0]->y; - curY2 = feats[3]->y; + cur_1 = features.front().pos; + cur_2 = features.back().pos; - // Make sure p1 < p2 - if (curX1 > curX2) std::swap(curX1,curX2); - if (curY1 > curY2) std::swap(curY1,curY2); -} - -void VisualToolClip::CommitDrag(feature_iterator) { CommitHold(); } void VisualToolClip::SetFeaturePositions() { - // Top-left - int i = 0; - feats[i]->x = curX1; - feats[i]->y = curY1; - i++; - - // Top-right - feats[i]->x = curX2; - feats[i]->y = curY1; - i++; - - // Bottom-left - feats[i]->x = curX1; - feats[i]->y = curY2; - i++; - - // Bottom-right - feats[i]->x = curX2; - feats[i]->y = curY2; + feature_iterator 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 + it->pos = cur_2; // Bottom-right } void VisualToolClip::DoRefresh() { - if (curDiag) { - GetLineClip(curDiag,curX1,curY1,curX2,curY2,inverse); + if (active_line) { + GetLineClip(active_line, cur_1, cur_2, inverse); + cur_1 = FromScriptCoords(cur_1); + cur_2 = FromScriptCoords(cur_2); SetFeaturePositions(); } } diff --git a/aegisub/src/visual_tool_clip.h b/aegisub/src/visual_tool_clip.h index 58df9ff6a..658d63e05 100644 --- a/aegisub/src/visual_tool_clip.h +++ b/aegisub/src/visual_tool_clip.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -39,16 +26,10 @@ /// @class ClipCorner /// @brief VisualDraggableFeature with siblings -class ClipCorner : public VisualDraggableFeature { -public: - ClipCorner* horiz; /// Other corner on this corner's horizontal line - ClipCorner* vert; /// Other corner on this corner's vertical line - /// @brief Constructor - ClipCorner() - : VisualDraggableFeature() - , horiz(NULL) - , vert(NULL) - { } +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) { } }; /// DOCME @@ -57,11 +38,10 @@ public: /// /// DOCME class VisualToolClip : public VisualTool { - int startX,startY,curX1,curY1,curX2,curY2; + Vector2D cur_1; + Vector2D cur_2; - ClipCorner *feats[4]; - - bool inverse; + bool inverse; ///< Is this currently in iclip mode? bool InitializeHold(); void UpdateHold(); @@ -72,9 +52,8 @@ class VisualToolClip : public VisualTool { bool InitializeDrag(feature_iterator feature); void UpdateDrag(feature_iterator feature); - void CommitDrag(feature_iterator feature); void Draw(); public: - VisualToolClip(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *); + VisualToolClip(VideoDisplay *parent, agi::Context *context); }; diff --git a/aegisub/src/visual_tool_cross.cpp b/aegisub/src/visual_tool_cross.cpp index 33933c29e..a8fb4e02e 100644 --- a/aegisub/src/visual_tool_cross.cpp +++ b/aegisub/src/visual_tool_cross.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -35,88 +22,81 @@ #include "config.h" -#include "ass_file.h" -#include "include/aegisub/context.h" -#include "gl_text.h" -#include "video_context.h" -#include "video_display.h" #include "visual_tool_cross.h" -VisualToolCross::VisualToolCross(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *) -: VisualTool(parent, context, video) -, glText(new OpenGLText) +#include "gl_text.h" +#include "include/aegisub/context.h" +#include "video_display.h" + +VisualToolCross::VisualToolCross(VideoDisplay *parent, agi::Context *context) +: VisualTool(parent, context) +, gl_text(new OpenGLText) { + parent->SetCursor(wxCursor(wxCURSOR_BLANK)); } -bool VisualToolCross::Update() { - if (!leftDClick) return true; - if (!curDiag) return true; +VisualToolCross::~VisualToolCross() { + parent->SetCursor(wxNullCursor); +} - int dx, dy; - int vx = video.x; - int vy = video.y; - GetLinePosition(curDiag, dx, dy); - parent->ToScriptCoords(&vx, &vy); - parent->ToScriptCoords(&dx, &dy); - dx -= vx; - dy -= vy; +void VisualToolCross::OnDoubleClick() { + Vector2D d = ToScriptCoords(mouse_pos) - GetLinePosition(active_line); Selection sel = c->selectionController->GetSelectedSet(); - for (Selection::const_iterator cur = sel.begin(); cur != sel.end(); ++cur) { - int x1, y1; - GetLinePosition(*cur, x1, y1); - parent->ToScriptCoords(&x1, &y1); - SetOverride(*cur, "\\pos", wxString::Format("(%i,%i)", x1 - dx, y1 - dy)); + for (Selection::const_iterator it = sel.begin(); it != sel.end(); ++it) { + Vector2D p1, p2; + int t1, t2; + if (GetLineMove(*it, p1, p2, t1, t2)) { + if (t1 > 0 || t2 > 0) + SetOverride(*it, "\\move", wxString::Format("(%s,%s,%d,%d)", (p1 + d).Str(), (p2 + d).Str(), t1, t2)); + else + SetOverride(*it, "\\move", wxString::Format("(%s,%s)", (p1 + d).Str(), (p2 + d).Str())); + } + else + SetOverride(*it, "\\pos", (GetLinePosition(*it) + d).PStr()); + + if (Vector2D org = GetLineOrigin(*it)) + SetOverride(*it, "\\org", (org + d).PStr()); } Commit(_("positioning")); - return false; } void VisualToolCross::Draw() { + if (!mouse_pos) return; + // Draw cross - glDisable(GL_LINE_SMOOTH); - glEnable(GL_COLOR_LOGIC_OP); - glLogicOp(GL_INVERT); - glLineWidth(1); - glBegin(GL_LINES); - glColor3f(1.0f,1.0f,1.0f); - glVertex2f(0, video.y); - glVertex2f(video.w, video.y); - glVertex2f(video.x, 0); - glVertex2f(video.x, video.h); - glEnd(); - glDisable(GL_COLOR_LOGIC_OP); + gl.SetInvert(); + gl.SetLineColour(*wxWHITE, 1.0, 1); + float lines[] = { + 0.f, mouse_pos.Y(), + video_res.X(), mouse_pos.Y(), + mouse_pos.X(), 0.f, + mouse_pos.X(), video_res.Y() + }; + gl.DrawLines(2, lines, 4); + gl.ClearInvert(); - int tx,ty; - if (!wxGetMouseState().ShiftDown()) { - tx = video.x; - ty = video.y; - } - else { - tx = video.w - video.x; - ty = video.h - video.y; - } - parent->ToScriptCoords(&tx, &ty); - wxString mouseText = wxString::Format("%i,%i", tx, ty); + Vector2D t = ToScriptCoords(shift_down ? video_res - mouse_pos : mouse_pos); + wxString mouse_text = t.Str(); - int tw,th; - glText->SetFont("Verdana", 12, true, false); - glText->SetColour(wxColour(255, 255, 255), 1.f); - glText->GetExtent(mouseText, tw, th); + int tw, th; + gl_text->SetFont("Verdana", 12, true, false); + gl_text->SetColour(*wxWHITE, 1.f); + gl_text->GetExtent(mouse_text, tw, th); - // Calculate draw position - int dx = video.x; - int dy = video.y; - bool left = dx > video.w / 2; - bool bottom = dy < video.h / 2; + // Place the text in the corner of the cross closest to the center of the video + int dx = mouse_pos.X(); + int dy = mouse_pos.Y(); + if (dx > video_res.X() / 2) + dx -= tw + 4; + else + dx += 4; - // Text draw coords - if (left) dx -= tw + 4; - else dx += 4; - if (bottom) dy += 3; - else dy -= th + 3; + if (dy < video_res.Y() / 2) + dy += 3; + else + dy -= th + 3; - // Draw text - glText->Print(mouseText, dx, dy); + gl_text->Print(mouse_text, dx, dy); } diff --git a/aegisub/src/visual_tool_cross.h b/aegisub/src/visual_tool_cross.h index 873bbc12b..07cd8e032 100644 --- a/aegisub/src/visual_tool_cross.h +++ b/aegisub/src/visual_tool_cross.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -34,24 +21,21 @@ /// @ingroup visual_ts /// -#ifndef AGI_PRE -#include -#endif +#include #include "visual_feature.h" #include "visual_tool.h" class OpenGLText; -/// DOCME /// @class VisualToolCross -/// @brief DOCME -/// -/// DOCME +/// @brief A crosshair which shows the current mouse position and on double-click +/// shifts the selected lines to the clicked point class VisualToolCross : public VisualTool { - bool Update(); + void OnDoubleClick(); void Draw(); - std::tr1::shared_ptr glText; + agi::scoped_ptr gl_text; public: - VisualToolCross(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *); + VisualToolCross(VideoDisplay *parent, agi::Context *context); + ~VisualToolCross(); }; diff --git a/aegisub/src/visual_tool_drag.cpp b/aegisub/src/visual_tool_drag.cpp index 389e4c3bc..a1c794823 100644 --- a/aegisub/src/visual_tool_drag.cpp +++ b/aegisub/src/visual_tool_drag.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -35,83 +22,79 @@ #include "config.h" +#include "visual_tool_drag.h" + +#ifndef AGI_PRE +#include +#include +#endif + #include "ass_dialogue.h" #include "ass_file.h" #include "include/aegisub/context.h" #include "libresrc/libresrc.h" -#include "subs_grid.h" #include "utils.h" #include "video_context.h" -#include "video_display.h" -#include "visual_tool_drag.h" -enum { - BUTTON_TOGGLE_MOVE = VISUAL_SUB_TOOL_START -}; static const DraggableFeatureType DRAG_ORIGIN = DRAG_BIG_TRIANGLE; static const DraggableFeatureType DRAG_START = DRAG_BIG_SQUARE; static const DraggableFeatureType DRAG_END = DRAG_BIG_CIRCLE; -/// @brief Constructor -/// @param _parent -/// @param toolBar -VisualToolDrag::VisualToolDrag(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar * toolBar) -: VisualTool(parent, context, video) -, toolBar(toolBar) -, primary(NULL) -, toggleMoveOnMove(true) +VisualToolDrag::VisualToolDrag(VideoDisplay *parent, agi::Context *context) +: VisualTool(parent, context) +, primary(0) +, button_is_move(true) { - toolBar->AddTool(BUTTON_TOGGLE_MOVE, _("Toggle between \\move and \\pos"), GETIMAGE(visual_move_conv_move_24)); - toolBar->Realize(); - toolBar->Show(true); - c->selectionController->GetSelectedSet(selection); - OnFileChanged(); +} + +void VisualToolDrag::SetToolbar(wxToolBar *tb) { + toolbar = tb; + toolbar->AddTool(-1, _("Toggle between \\move and \\pos"), GETIMAGE(visual_move_conv_move_24)); + toolbar->Realize(); + toolbar->Show(true); + + toolbar->Bind(wxEVT_COMMAND_TOOL_CLICKED, &VisualToolDrag::OnSubTool, this); } void VisualToolDrag::UpdateToggleButtons() { - // Check which bitmap to use - bool toMove = true; - if (curDiag) { - int x1,y1,x2,y2,t1,t2; - bool hasMove; - GetLineMove(curDiag,hasMove,x1,y1,x2,y2,t1,t2); - toMove = !hasMove; + bool to_move = true; + if (active_line) { + Vector2D p1, p2; + int t1, t2; + to_move = !GetLineMove(active_line, p1, p2, t1, t2); } - // No change needed - if (toMove == toggleMoveOnMove) return; + if (to_move == button_is_move) return; - // Change bitmap - if (toMove) { - toolBar->SetToolNormalBitmap(BUTTON_TOGGLE_MOVE, GETIMAGE(visual_move_conv_move_24)); - } - else { - toolBar->SetToolNormalBitmap(BUTTON_TOGGLE_MOVE, GETIMAGE(visual_move_conv_pos_24)); - } - toggleMoveOnMove = toMove; + toolbar->SetToolNormalBitmap(toolbar->GetToolByPos(0)->GetId(), + to_move ? GETIMAGE(visual_move_conv_move_24) : GETIMAGE(visual_move_conv_pos_24)); + button_is_move = to_move; } -/// @brief Toggle button pressed -/// @param event void VisualToolDrag::OnSubTool(wxCommandEvent &) { // Toggle \move <-> \pos + VideoContext *vc = c->videoController; for (Selection::const_iterator cur = selection.begin(); cur != selection.end(); ++cur) { AssDialogue *line = *cur; - int x1,y1,x2,y2,t1,t2; - bool hasMove; + Vector2D p1, p2; + int t1, t2; - GetLinePosition(line,x1,y1); - GetLineMove(line,hasMove,x1,y1,x2,y2,t1,t2); - parent->ToScriptCoords(&x1, &y1); - parent->ToScriptCoords(&x2, &y2); + bool has_move = GetLineMove(line, p1, p2, t1, t2); - if (hasMove) SetOverride(line, "\\pos",wxString::Format("(%i,%i)",x1,y1)); - else SetOverride(line, "\\move",wxString::Format("(%i,%i,%i,%i,%i,%i)",x1,y1,x1,y1,0,line->End.GetMS() - line->Start.GetMS())); + if (has_move) + SetOverride(line, "\\pos", p1.PStr()); + else { + p1 = GetLinePosition(line); + // Round the start and end times to exact frames + int start = vc->TimeAtFrame(vc->FrameAtTime(line->Start.GetMS(), agi::vfr::START)) - line->Start.GetMS(); + int end = vc->TimeAtFrame(vc->FrameAtTime(line->Start.GetMS(), agi::vfr::END)) - line->Start.GetMS(); + SetOverride(line, "\\move", wxString::Format("(%s,%s,%d,%d)", p1.Str(), p1.Str(), start, end)); + } } Commit(); - Refresh(); + OnFileChanged(); UpdateToggleButtons(); } @@ -122,161 +105,142 @@ void VisualToolDrag::OnLineChanged() { void VisualToolDrag::OnFileChanged() { /// @todo it should be possible to preserve the selection in some cases features.clear(); - ClearSelection(); - primary = NULL; + sel_features.clear(); + primary = 0; + active_feature = features.end(); - for (int i = c->subsGrid->GetRows() - 1; i >=0; i--) { - AssDialogue *diag = c->subsGrid->GetDialogue(i); - if (c->subsGrid->IsDisplayed(diag)) { - MakeFeatures(diag); - } + for (entryIter it = c->ass->Line.begin(); it != c->ass->Line.end(); ++it) { + AssDialogue *diag = dynamic_cast(*it); + if (diag && IsDisplayed(diag)) + MakeFeatures(diag); } + UpdateToggleButtons(); } void VisualToolDrag::OnFrameChanged() { - if (primary && !c->subsGrid->IsDisplayed(primary->line)) primary = NULL; + if (primary && !IsDisplayed(primary->line)) + primary = 0; feature_iterator feat = features.begin(); feature_iterator end = features.end(); - for (int i = c->subsGrid->GetRows() - 1; i >=0; i--) { - AssDialogue *diag = c->subsGrid->GetDialogue(i); - if (c->subsGrid->IsDisplayed(diag)) { + for (entryIter it = c->ass->Line.begin(); it != c->ass->Line.end(); ++it) { + AssDialogue *diag = dynamic_cast(*it); + if (!diag) continue; + + if (IsDisplayed(diag)) { // Features don't exist and should - if (feat == end || feat->line != diag) { + if (feat == end || feat->line != diag) MakeFeatures(diag, feat); - } // Move past already existing features for the line - else { + else while (feat != end && feat->line == diag) ++feat; - } } else { // Remove all features for this line (if any) while (feat != end && feat->line == diag) { - feat->line = NULL; + feat->line = 0; RemoveSelection(feat); feat = features.erase(feat); } } } + + active_feature = features.end(); } void VisualToolDrag::OnSelectedSetChanged(const Selection &added, const Selection &removed) { c->selectionController->GetSelectedSet(selection); - if (!externalChange) return; - externalChange = false; - c->subsGrid->BeginBatch(); - for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) { - // Remove all deselected lines - if (removed.find(cur->line) != removed.end()) { - RemoveSelection(cur); - } - // And add all newly selected lines - else if (added.find(cur->line) != added.end() && cur->type == DRAG_START) { - AddSelection(cur); - } + for (feature_iterator it = features.begin(); it != features.end(); ++it) { + if (removed.count(it->line)) + sel_features.erase(it); + else if (added.count(it->line) && it->type == DRAG_START) + sel_features.insert(it); } - - c->subsGrid->EndBatch(); - externalChange = true; } void VisualToolDrag::Draw() { DrawAllFeatures(); - // Draw arrows + // Draw connecting lines for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) { if (cur->type == DRAG_START) continue; + feature_iterator p2 = cur; feature_iterator p1 = cur->parent; - // Has arrow? - bool hasArrow = p2->type == DRAG_END; - int arrowLen = hasArrow ? 10 : 0; + // Move end marker has an arrow; origin doesn't + bool has_arrow = p2->type == DRAG_END; + int arrow_len = has_arrow ? 10 : 0; - // See if the distance between them is enough - int dx = p2->x - p1->x; - int dy = p2->y - p1->y; - int dist = (int)sqrt(double(dx*dx + dy*dy)); - if (dist < 20+arrowLen) continue; + // Don't show the connecting line if the features are very close + Vector2D direction = p2->pos - p1->pos; + if (direction.SquareLen() < (20 + arrow_len) * (20 + arrow_len)) continue; - // Get end points - int x1 = p1->x + dx*10/dist; - int x2 = p2->x - dx*(10+arrowLen)/dist; - int y1 = p1->y + dy*10/dist; - int y2 = p2->y - dy*(10+arrowLen)/dist; + direction = direction.Unit(); + // Get the start and end points of the line + Vector2D start = p1->pos + direction * 10; + Vector2D end = p2->pos - direction * (10 + arrow_len); - // Draw arrow - if (hasArrow) { - // Calculate angle - double angle = atan2(double(y2-y1),double(x2-x1))+1.570796; - int sx = int(cos(angle)*4); - int sy = int(-sin(angle)*4); + if (has_arrow) { + gl.SetLineColour(colour[3], 0.8f, 2); // Arrow line - SetLineColour(colour[3],0.8f,2); - DrawLine(x1,y1,x2,y2); + gl.DrawLine(start, end); // Arrow head - DrawLine(x2+sx,y2-sy,x2-sx,y2+sy); - DrawLine(x2+sx,y2-sy,x2+dx*10/dist,y2+dy*10/dist); - DrawLine(x2-sx,y2+sy,x2+dx*10/dist,y2+dy*10/dist); + Vector2D t_half_base_w = Vector2D(-direction.Y(), direction.X()) * 4; + gl.DrawTriangle(end + direction * arrow_len, end + t_half_base_w, end - t_half_base_w); } - // Draw dashed line else { - SetLineColour(colour[3],0.5f,2); - DrawDashedLine(x1,y1,x2,y2,6); + gl.SetLineColour(colour[3], 0.5f, 2); + gl.DrawDashedLine(start, end, 6); } } } + void VisualToolDrag::MakeFeatures(AssDialogue *diag) { MakeFeatures(diag, features.end()); } + void VisualToolDrag::MakeFeatures(AssDialogue *diag, feature_iterator pos) { - // Get position - int x1,x2,y1,y2; - int t1=0; - int t2=diag->End.GetMS()-diag->Start.GetMS(); - int torgx,torgy; - bool hasMove; - GetLinePosition(diag,x1,y1,torgx,torgy); - GetLineMove(diag,hasMove,x1,y1,x2,y2,t1,t2); + Vector2D p1 = FromScriptCoords(GetLinePosition(diag)); // Create \pos feature Feature feat; - feat.x = x1; - feat.y = y1; + feat.pos = p1; feat.layer = 0; feat.type = DRAG_START; - feat.time = t1; + feat.time = 0; feat.line = diag; feat.parent = features.end(); features.insert(pos, feat); feature_iterator cur = pos; --cur; feat.parent = cur; - if (selection.find(diag) != selection.end()) { - AddSelection(cur); - } + if (selection.count(diag)) + sel_features.insert(cur); + + Vector2D p2; + int t1, t2; // Create move destination feature - if (hasMove) { - feat.x = x2; - feat.y = y2; + 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 = --pos; ++pos; } + // Create org feature - if (torgx != x1 || torgy != y1) { - feat.x = torgx; - feat.y = torgy; + if (Vector2D org = GetLineOrigin(diag)) { + feat.pos = FromScriptCoords(org); feat.layer = -1; feat.type = DRAG_ORIGIN; feat.time = 0; @@ -291,94 +255,57 @@ bool VisualToolDrag::InitializeDrag(feature_iterator feature) { // Set time of clicked feature to the current frame and shift all other // selected features by the same amount if (feature->type != DRAG_ORIGIN) { - int time = c->videoController->TimeAtFrame(frameNumber) - feature->line->Start.GetMS(); + int time = c->videoController->TimeAtFrame(frame_number) - feature->line->Start.GetMS(); int change = time - feature->time; - for (sel_iterator cur = selectedFeatures.begin(); cur != selectedFeatures.end(); ++cur) { - if ((*cur)->type != DRAG_ORIGIN) { - (*cur)->time += change; - } + for (sel_iterator cur = sel_features.begin(); cur != sel_features.end(); ++cur) { + (*cur)->time += change; } } return true; } -void VisualToolDrag::CommitDrag(feature_iterator feature) { +void VisualToolDrag::UpdateDrag(feature_iterator feature) { if (feature->type == DRAG_ORIGIN) { - int x = feature->x; - int y = feature->y; - parent->ToScriptCoords(&x, &y); - SetOverride(feature->line, "\\org",wxString::Format("(%i,%i)",x,y)); + SetOverride(feature->line, "\\org", ToScriptCoords(feature->pos).PStr()); return; } - feature_iterator p = feature->parent; - if (feature->type == DRAG_END) { - std::swap(feature, p); - } + feature_iterator end_feature = feature->parent; + if (feature->type == DRAG_END) + std::swap(feature, end_feature); - int x1 = feature->x; - int y1 = feature->y; - parent->ToScriptCoords(&x1, &y1); - - // Position - if (feature->parent == features.end()) { - SetOverride(feature->line, "\\pos", wxString::Format("(%i,%i)", x1, y1)); - } - // Move - else { - int x2 = p->x; - int y2 = p->y; - parent->ToScriptCoords(&x2, &y2); - - SetOverride(feature->line, "\\move", wxString::Format("(%i,%i,%i,%i,%i,%i)", x1, y1, x2, y2, feature->time, p->time)); - } + if (feature->parent == features.end()) + SetOverride(feature->line, "\\pos", ToScriptCoords(feature->pos).PStr()); + else + SetOverride(feature->line, "\\move", + wxString::Format("(%s,%s,%d,%d)", + ToScriptCoords(feature->pos).Str(), + ToScriptCoords(end_feature->pos).Str(), + feature->time, end_feature->time)); } -bool VisualToolDrag::Update() { - if (!leftDClick) return false; - int dx, dy; - int vx = video.x; - int vy = video.y; - parent->ToScriptCoords(&vx, &vy); - if (primary) { - dx = primary->x; - dy = primary->y; - } - else { - if (!curDiag) return false; - GetLinePosition(curDiag, dx, dy); - } - parent->ToScriptCoords(&dx, &dy); - dx -= vx; - dy -= vy; +void VisualToolDrag::OnDoubleClick() { + Vector2D d = ToScriptCoords(mouse_pos) - (primary ? ToScriptCoords(primary->pos) : GetLinePosition(active_line)); - for (Selection::const_iterator cur = selection.begin(); cur != selection.end(); ++cur) { - int x1 = 0, y1 = 0, x2 = 0, y2 = 0, t1 = INT_MIN, t2 = INT_MIN, orgx, orgy; - bool isMove; - - GetLinePosition(*cur, x1, y1, orgx, orgy); - GetLineMove(*cur, isMove, x1, y1, x2, y2, t1, t2); - parent->ToScriptCoords(&x1, &y1); - parent->ToScriptCoords(&x2, &y2); - parent->ToScriptCoords(&orgx, &orgy); - - if (isMove) { - if (t1 > INT_MIN && t2 > INT_MIN) - SetOverride(*cur, "\\move", wxString::Format("(%i,%i,%i,%i,%i,%i)", x1 - dx, y1 - dy, x2 - dx, y2 - dy, t1, t2)); + Selection sel = c->selectionController->GetSelectedSet(); + for (Selection::const_iterator it = sel.begin(); it != sel.end(); ++it) { + Vector2D p1, p2; + int t1, t2; + if (GetLineMove(*it, p1, p2, t1, t2)) { + if (t1 > 0 || t2 > 0) + SetOverride(*it, "\\move", wxString::Format("(%s,%s,%d,%d)", (p1 + d).Str(), (p2 + d).Str(), t1, t2)); else - SetOverride(*cur, "\\move", wxString::Format("(%i,%i,%i,%i)", x1, y1, x2, y2)); - } - else { - SetOverride(*cur, "\\pos", wxString::Format("(%i,%i)", x1 - dx, y1 - dy)); - } - if (orgx != x1 || orgy != y1) { - SetOverride(*cur, "\\org", wxString::Format("(%i,%i)", orgx - dx, orgy - dy)); + SetOverride(*it, "\\move", wxString::Format("(%s,%s)", (p1 + d).Str(), (p2 + d).Str())); } + else + SetOverride(*it, "\\pos", (GetLinePosition(*it) + d).PStr()); + + if (Vector2D org = GetLineOrigin(*it)) + SetOverride(*it, "\\org", (org + d).PStr()); } Commit(_("positioning")); OnFileChanged(); - return false; } diff --git a/aegisub/src/visual_tool_drag.h b/aegisub/src/visual_tool_drag.h index d3f9029c3..6462c1a8c 100644 --- a/aegisub/src/visual_tool_drag.h +++ b/aegisub/src/visual_tool_drag.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -34,11 +21,6 @@ /// @ingroup visual_ts /// -#ifndef AGI_PRE -#include -#include -#endif - #include "visual_feature.h" #include "visual_tool.h" @@ -51,27 +33,26 @@ public: VisualToolDragDraggableFeature() : VisualDraggableFeature(), time(0) { } }; +class wxBitmapButton; +class wxToolBar; /// DOCME /// @class VisualToolDrag -/// @brief DOCME -/// -/// DOCME +/// @brief Moveable features for the positions of each visible line class VisualToolDrag : public VisualTool { /// The subtoolbar for the move/pos conversion button - wxToolBar *toolBar; + wxToolBar *toolbar; /// The feature last clicked on for the double-click handler /// Equal to curFeature during drags; possibly different at all other times - /// Null if no features have been clicked on or the last clicked on one no + /// NNULL if no features have been clicked on or the last clicked on one no /// longer exists Feature *primary; /// The last announced selection set Selection selection; - int change; /// When the button is pressed, will it convert the line to a move (vs. from /// move to pos)? Used to avoid changing the button's icon unnecessarily - bool toggleMoveOnMove; + bool button_is_move; /// @brief Create the features for a line /// @param diag Line to create the features for @@ -79,23 +60,23 @@ class VisualToolDrag : public VisualTool { void MakeFeatures(AssDialogue *diag, feature_iterator pos); void MakeFeatures(AssDialogue *diag); - bool InitializeDrag(feature_iterator feature); - void CommitDrag(feature_iterator feature); - - /// Set the pos/move button to the correct icon based on the active line - void UpdateToggleButtons(); - // Overriding SubtitleSelectionListener inherited from base VisualTool<> void OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed); void OnFrameChanged(); void OnFileChanged(); void OnLineChanged(); + void OnCoordinateSystemsChanged() { OnFileChanged(); } + bool InitializeDrag(feature_iterator feature); + void UpdateDrag(feature_iterator feature); void Draw(); - bool Update(); -public: - VisualToolDrag(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *toolbar); + void OnDoubleClick(); + /// Set the pos/move button to the correct icon based on the active line + void UpdateToggleButtons(); void OnSubTool(wxCommandEvent &event); +public: + VisualToolDrag(VideoDisplay *parent, agi::Context *context); + void SetToolbar(wxToolBar *tb); }; diff --git a/aegisub/src/visual_tool_rotatexy.cpp b/aegisub/src/visual_tool_rotatexy.cpp index ff9ab601b..fa4645dc9 100644 --- a/aegisub/src/visual_tool_rotatexy.cpp +++ b/aegisub/src/visual_tool_rotatexy.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -36,175 +23,152 @@ #include "config.h" #ifndef AGI_PRE -#include +#include #endif -#include "ass_dialogue.h" -#include "ass_file.h" -#include "include/aegisub/context.h" -#include "utils.h" -#include "video_context.h" -#include "video_display.h" #include "visual_tool_rotatexy.h" -VisualToolRotateXY::VisualToolRotateXY(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *) -: VisualTool(parent, context, video) +VisualToolRotateXY::VisualToolRotateXY(VideoDisplay *parent, agi::Context *context) +: VisualTool(parent, context) { features.resize(1); org = &features.back(); org->type = DRAG_BIG_TRIANGLE; - DoRefresh(); } void VisualToolRotateXY::Draw() { - if (!curDiag) return; + if (!active_line) return; - // Pivot coordinates - int dx=0,dy=0; - if (dragging) GetLinePosition(curDiag,dx,dy); - else GetLinePosition(curDiag,dx,dy,org->x,org->y); - dx = org->x; - dy = org->y; - - SetLineColour(colour[0]); - SetFillColour(colour[1],0.3f); - - // Draw pivot DrawAllFeatures(); // Transform grid - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glTranslatef(dx,dy,0.f); - float matrix[16] = { 2500, 0, 0, 0, 0, 2500, 0, 0, 0, 0, 1, 1, 0, 0, 2500, 2500 }; - glMultMatrixf(matrix); - glScalef(1.f,1.f,8.f); - if (curAngleY != 0.f) glRotatef(curAngleY,0.f,-1.f,0.f); - if (curAngleX != 0.f) glRotatef(curAngleX,-1.f,0.f,0.f); - if (curAngleZ != 0.f) glRotatef(curAngleZ,0.f,0.f,-1.f); + gl.SetOrigin(org->pos); + gl.SetRotation(angle_x, angle_y, angle_z); // Draw grid - glShadeModel(GL_SMOOTH); - SetLineColour(colour[0],0.5f,2); - SetModeLine(); - float r = colour[0].Red()/255.f; - float g = colour[0].Green()/255.f; - float b = colour[0].Blue()/255.f; - glBegin(GL_LINES); - for (int i=0;i<11;i++) { - float a = 1.f - abs(i-5)*0.18f; - int pos = 20*(i-5); - glColor4f(r,g,b,0.f); - glVertex2i(pos,120); - glColor4f(r,g,b,a); - glVertex2i(pos,0); - glVertex2i(pos,0); - glColor4f(r,g,b,0.f); - glVertex2i(pos,-120); - glVertex2i(120,pos); - glColor4f(r,g,b,a); - glVertex2i(0,pos); - glVertex2i(0,pos); - glColor4f(r,g,b,0.f); - glVertex2i(-120,pos); + gl.SetLineColour(colour[0], 0.5f, 2); + gl.SetModeLine(); + float r = colour[0].Red() / 255.f; + float g = colour[0].Green() / 255.f; + float b = colour[0].Blue() / 255.f; + + std::vector colors(11 * 8 * 4); + for (int i = 0; i < 88; ++i) { + colors[i * 4 + 0] = r; + colors[i * 4 + 1] = g; + colors[i * 4 + 2] = b; + colors[i * 4 + 3] = (i + 3) % 4 > 1 ? 0 : (1.f - abs(i / 8 - 5) * 0.18f); } - glEnd(); + + std::vector points(11 * 8 * 2); + for (int i = 0; i < 11; ++i) { + int pos = 20 * (i - 5); + + points[i * 16 + 0] = pos; + points[i * 16 + 1] = 120; + + points[i * 16 + 2] = pos; + points[i * 16 + 3] = 0; + + points[i * 16 + 4] = pos; + points[i * 16 + 5] = 0; + + points[i * 16 + 6] = pos; + points[i * 16 + 7] = -120; + + points[i * 16 + 8] = 120; + points[i * 16 + 9] = pos; + + points[i * 16 + 10] = 0; + points[i * 16 + 11] = pos; + + points[i * 16 + 12] = 0; + points[i * 16 + 13] = pos; + + points[i * 16 + 14] = -120; + points[i * 16 + 15] = pos; + } + + gl.DrawLines(2, points, 4, colors); // Draw vectors - SetLineColour(colour[3],1.f,2); - SetModeLine(); - glBegin(GL_LINES); - glVertex3f(0.f,0.f,0.f); - glVertex3f(50.f,0.f,0.f); - glVertex3f(0.f,0.f,0.f); - glVertex3f(0.f,50.f,0.f); - glVertex3f(0.f,0.f,0.f); - glVertex3f(0.f,0.f,50.f); - glEnd(); + gl.SetLineColour(colour[3], 1.f, 2); + float vectors[] = { + 0.f, 0.f, 0.f, + 50.f, 0.f, 0.f, + 0.f, 0.f, 0.f, + 0.f, 50.f, 0.f, + 0.f, 0.f, 0.f, + 0.f, 0.f, 50.f, + }; + gl.DrawLines(3, vectors, 6); // Draw arrow tops - glBegin(GL_TRIANGLE_FAN); - glVertex3f(60.f,0.f,0.f); - glVertex3f(50.f,-3.f,-3.f); - glVertex3f(50.f,3.f,-3.f); - glVertex3f(50.f,3.f,3.f); - glVertex3f(50.f,-3.f,3.f); - glVertex3f(50.f,-3.f,-3.f); - glEnd(); - glBegin(GL_TRIANGLE_FAN); - glVertex3f(0.f,60.f,0.f); - glVertex3f(-3.f,50.f,-3.f); - glVertex3f(3.f,50.f,-3.f); - glVertex3f(3.f,50.f,3.f); - glVertex3f(-3.f,50.f,3.f); - glVertex3f(-3.f,50.f,-3.f); - glEnd(); - glBegin(GL_TRIANGLE_FAN); - glVertex3f(0.f,0.f,60.f); - glVertex3f(-3.f,-3.f,50.f); - glVertex3f(3.f,-3.f,50.f); - glVertex3f(3.f,3.f,50.f); - glVertex3f(-3.f,3.f,50.f); - glVertex3f(-3.f,-3.f,50.f); - glEnd(); + float arrows[] = { + 60.f, 0.f, 0.f, + 50.f, -3.f, -3.f, + 50.f, 3.f, -3.f, + 50.f, 3.f, 3.f, + 50.f, -3.f, 3.f, + 50.f, -3.f, -3.f, - glPopMatrix(); - glShadeModel(GL_FLAT); + 0.f, 60.f, 0.f, + -3.f, 50.f, -3.f, + 3.f, 50.f, -3.f, + 3.f, 50.f, 3.f, + -3.f, 50.f, 3.f, + -3.f, 50.f, -3.f, + + 0.f, 0.f, 60.f, + -3.f, -3.f, 50.f, + 3.f, -3.f, 50.f, + 3.f, 3.f, 50.f, + -3.f, 3.f, 50.f, + -3.f, -3.f, 50.f, + }; + + gl.DrawLines(3, arrows, 18); + + gl.ResetTransform(); } bool VisualToolRotateXY::InitializeHold() { - startAngleX = (org->y-video.y)*2.f; - startAngleY = (video.x-org->x)*2.f; - origAngleX = curAngleX; - origAngleY = curAngleY; + orig_x = angle_x; + orig_y = angle_y; return true; } void VisualToolRotateXY::UpdateHold() { - float screenAngleX = (org->y-video.y)*2.f; - float screenAngleY = (video.x-org->x)*2.f; + Vector2D delta = (mouse_pos - drag_start) * 2; + if (shift_down) + delta = delta.SingleAxis(); - // Deltas - float deltaX = screenAngleX - startAngleX; - float deltaY = screenAngleY - startAngleY; - if (shiftDown) { - if (fabs(deltaX) >= fabs(deltaY)) deltaY = 0.f; - else deltaX = 0.f; + angle_x = orig_x - delta.Y(); + angle_y = orig_y + delta.X(); + + if (ctrl_down) { + angle_x = floorf(angle_x / 30.f + .5f) * 30.f; + angle_y = floorf(angle_y / 30.f + .5f) * 30.f; } - // Calculate - curAngleX = fmodf(deltaX + origAngleX + 360.f, 360.f); - curAngleY = fmodf(deltaY + origAngleY + 360.f, 360.f); + angle_x = fmodf(angle_x + 360.f, 360.f); + angle_y = fmodf(angle_y + 360.f, 360.f); - // Oh Snap - if (ctrlDown) { - curAngleX = floorf(curAngleX/30.f+.5f)*30.f; - curAngleY = floorf(curAngleY/30.f+.5f)*30.f; - if (curAngleX > 359.f) curAngleX = 0.f; - if (curAngleY > 359.f) curAngleY = 0.f; - } + SetSelectedOverride("\\frx", wxString::Format("(%0.3g)", angle_x)); + SetSelectedOverride("\\fry", wxString::Format("(%0.3g)", angle_y)); } -void VisualToolRotateXY::CommitHold() { - Selection sel = c->selectionController->GetSelectedSet(); - for (Selection::const_iterator cur = sel.begin(); cur != sel.end(); ++cur) { - SetOverride(*cur, "\\frx",wxString::Format("(%0.3g)",curAngleX)); - SetOverride(*cur, "\\fry",wxString::Format("(%0.3g)",curAngleY)); - } -} - -void VisualToolRotateXY::CommitDrag(feature_iterator feature) { - int x = feature->x; - int y = feature->y; - parent->ToScriptCoords(&x, &y); - SetOverride(curDiag, "\\org",wxString::Format("(%i,%i)",x,y)); +void VisualToolRotateXY::UpdateDrag(feature_iterator feature) { + SetOverride(active_line, "\\org", ToScriptCoords(feature->pos).PStr()); } void VisualToolRotateXY::DoRefresh() { - if (!curDiag) return; - int posx, posy; - GetLinePosition(curDiag,posx,posy,org->x,org->y); - GetLineRotation(curDiag,curAngleX,curAngleY,curAngleZ); + if (!active_line) return; + + if (!(org->pos = GetLineOrigin(active_line))) + org->pos = GetLinePosition(active_line); + org->pos = FromScriptCoords(org->pos); + + GetLineRotation(active_line, angle_x, angle_y, angle_z); } diff --git a/aegisub/src/visual_tool_rotatexy.h b/aegisub/src/visual_tool_rotatexy.h index 25d00fec5..f64ff95f2 100644 --- a/aegisub/src/visual_tool_rotatexy.h +++ b/aegisub/src/visual_tool_rotatexy.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -41,20 +28,20 @@ /// @class VisualToolRotateXY /// @brief DOCME class VisualToolRotateXY : public VisualTool { - float curAngleX,startAngleX,origAngleX; - float curAngleY,startAngleY,origAngleY; - float curAngleZ; + float angle_x; /// Current x rotation + float angle_y; /// Current y rotation + float angle_z; /// Current z rotation + + float orig_x; ///< x rotation at the beginning of the current hold + float orig_y; ///< y rotation at the beginning of the current hold + Feature *org; + void DoRefresh(); + void Draw(); + void UpdateDrag(feature_iterator feature); bool InitializeHold(); void UpdateHold(); - void CommitHold(); - - void CommitDrag(feature_iterator feature); - - void DoRefresh(); - - void Draw(); public: - VisualToolRotateXY(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *); + VisualToolRotateXY(VideoDisplay *parent, agi::Context *context); }; diff --git a/aegisub/src/visual_tool_rotatez.cpp b/aegisub/src/visual_tool_rotatez.cpp index 06191fddc..f280e0a00 100644 --- a/aegisub/src/visual_tool_rotatez.cpp +++ b/aegisub/src/visual_tool_rotatez.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -39,133 +26,110 @@ #include #endif -#include "ass_dialogue.h" -#include "ass_file.h" -#include "include/aegisub/context.h" -#include "utils.h" -#include "video_context.h" -#include "video_display.h" #include "visual_tool_rotatez.h" +#include "utils.h" + static const float deg2rad = 3.1415926536f / 180.f; static const float rad2deg = 180.f / 3.1415926536f; -VisualToolRotateZ::VisualToolRotateZ(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *) -: VisualTool(parent, context, video) +VisualToolRotateZ::VisualToolRotateZ(VideoDisplay *parent, agi::Context *context) +: VisualTool(parent, context) { features.resize(1); org = &features.back(); org->type = DRAG_BIG_TRIANGLE; - DoRefresh(); } void VisualToolRotateZ::Draw() { - if (!curDiag) return; + if (!active_line) return; - // Draw pivot DrawAllFeatures(); - int radius = (int)sqrt(double((posx-org->x)*(posx-org->x)+(posy-org->y)*(posy-org->y))); - int oRadius = radius; - if (radius < 50) radius = 50; - - int deltax = int(cos(curAngle*deg2rad)*radius); - int deltay = int(-sin(curAngle*deg2rad)*radius); - - // Set colours - SetLineColour(colour[0]); - SetFillColour(colour[1],0.3f); + float radius = (pos - org->pos).Len(); + float oRadius = radius; + if (radius < 50) + radius = 50; // Set up the projection - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glTranslatef(org->x,org->y,-1.f); - float matrix[16] = { 2500, 0, 0, 0, 0, 2500, 0, 0, 0, 0, 1, 1, 0, 0, 2500, 2500 }; - glMultMatrixf(matrix); - glScalef(1.f,1.f,8.f); - glRotatef(ry,0.f,-1.f,0.f); - glRotatef(rx,-1.f,0.f,0.f); - glScalef(scaleX/100.f,scaleY/100.f,1.f); + gl.SetOrigin(org->pos); + gl.SetRotation(rotation_x, rotation_y, 0); + gl.SetScale(scale); // Draw the circle - DrawRing(0,0,radius+4,radius-4); + gl.SetLineColour(colour[0]); + gl.SetFillColour(colour[1], 0.3f); + gl.DrawRing(Vector2D(), radius + 4, radius - 4); // Draw markers around circle int markers = 6; float markStart = -90.f / markers; - float markEnd = markStart+(180.f/markers); - for (int i=0;ix != posx || org->y != posy) { - double angle = atan2(double(org->y-posy),double(posx-org->x)) + curAngle*deg2rad; - int fx = int(cos(angle)*oRadius); - int fy = -int(sin(angle)*oRadius); - DrawLine(0,0,fx,fy); - int mdx = int(cos(curAngle*deg2rad)*20); - int mdy = int(-sin(curAngle*deg2rad)*20); - DrawLine(fx-mdx,fy-mdy,fx+mdx,fy+mdy); + if (org->pos != pos) { + Vector2D rotated_pos = Vector2D::FromAngle(angle * deg2rad - (pos - org->pos).Angle()) * oRadius; + + // Draw the line from origin to rotated position + gl.DrawLine(Vector2D(), rotated_pos); + + // Draw the line under the text + gl.DrawLine(rotated_pos - angle_vec * 20, rotated_pos + angle_vec * 20); } - // Draw the rotation line - SetLineColour(colour[0],1.f,1); - SetFillColour(colour[1],0.3f); - DrawCircle(deltax,deltay,4); + // Draw the fake features on the ring + gl.SetLineColour(colour[0], 1.f, 1); + gl.SetFillColour(colour[1], 0.3f); + gl.DrawCircle(angle_vec * radius, 4); + gl.DrawCircle(angle_vec * -radius, 4); - glPopMatrix(); + // Clear the projection + gl.ResetTransform(); - // Draw line to mouse - if (!dragging && curFeature == features.end() && video.x > INT_MIN && video.y > INT_MIN) { - SetLineColour(colour[0]); - DrawLine(org->x,org->y,video.x,video.y); + // Draw line to mouse if it isn't over the origin feature + if (mouse_pos && (mouse_pos - org->pos).SquareLen() > 100) { + gl.SetLineColour(colour[0]); + gl.DrawLine(org->pos, mouse_pos); } } bool VisualToolRotateZ::InitializeHold() { - startAngle = atan2(double(org->y-video.y),double(video.x-org->x)) * rad2deg; - origAngle = curAngle; - curDiag->StripTag("\\frz"); - curDiag->StripTag("\\fr"); - + orig_angle = angle + (org->pos - mouse_pos).Angle() * rad2deg; return true; } void VisualToolRotateZ::UpdateHold() { - float screenAngle = atan2(double(org->y-video.y),double(video.x-org->x)) * rad2deg; - curAngle = fmodf(screenAngle - startAngle + origAngle + 360.f, 360.f); + angle = orig_angle - (org->pos - mouse_pos).Angle() * rad2deg; - // Oh Snap - if (ctrlDown) { - curAngle = floorf(curAngle/30.f+.5f)*30.f; - if (curAngle > 359.f) curAngle = 0.f; - } + if (ctrl_down) + angle = floorf(angle / 30.f + .5f) * 30.f; + + angle = fmodf(angle + 360.f, 360.f); + + SetSelectedOverride("\\frz", wxString::Format("(%0.3g)", angle)); } -void VisualToolRotateZ::CommitHold() { - Selection sel = c->selectionController->GetSelectedSet(); - for (Selection::const_iterator cur = sel.begin(); cur != sel.end(); ++cur) { - SetOverride(*cur, "\\frz",wxString::Format("(%0.3g)",curAngle)); - } -} - -void VisualToolRotateZ::CommitDrag(feature_iterator feature) { - int x = feature->x; - int y = feature->y; - parent->ToScriptCoords(&x, &y); - SetOverride(curDiag, "\\org",wxString::Format("(%i,%i)",x,y)); +void VisualToolRotateZ::UpdateDrag(feature_iterator feature) { + SetOverride(active_line, "\\org", ToScriptCoords(feature->pos).PStr()); } void VisualToolRotateZ::DoRefresh() { - if (!curDiag) return; - GetLinePosition(curDiag, posx, posy, org->x, org->y); - GetLineRotation(curDiag, rx, ry, curAngle); - GetLineScale(curDiag, scaleX, scaleY); + if (!active_line) return; + + pos = FromScriptCoords(GetLinePosition(active_line)); + if (!(org->pos = GetLineOrigin(active_line))) + org->pos = pos; + else + org->pos = FromScriptCoords(org->pos); + + GetLineRotation(active_line, rotation_x, rotation_y, angle); + GetLineScale(active_line, scale); } diff --git a/aegisub/src/visual_tool_rotatez.h b/aegisub/src/visual_tool_rotatez.h index dd6b1d53c..b23cdee2c 100644 --- a/aegisub/src/visual_tool_rotatez.h +++ b/aegisub/src/visual_tool_rotatez.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -37,29 +24,30 @@ #include "visual_feature.h" #include "visual_tool.h" - /// DOCME /// @class VisualToolRotateZ /// @brief DOCME /// /// DOCME class VisualToolRotateZ : public VisualTool { - float curAngle, startAngle, origAngle; - Feature *org; - int posx, posy; - float rx, ry; - float scaleX, scaleY; + float angle; ///< Current Z rotation + float orig_angle; ///< Z rotation at the beginning of the current hold + Vector2D pos; ///< Position of the dialogue line + Vector2D scale; ///< Current scale + + float rotation_x; ///< Current X rotation + float rotation_y; ///< Current Y rotation + + Feature *org; ///< The origin feature bool InitializeHold(); void UpdateHold(); - void CommitHold(); - void CommitDrag(feature_iterator feature); + void UpdateDrag(feature_iterator feature); void DoRefresh(); void Draw(); - bool Update() { return true; } public: - VisualToolRotateZ(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *); + VisualToolRotateZ(VideoDisplay *parent, agi::Context *context); }; diff --git a/aegisub/src/visual_tool_scale.cpp b/aegisub/src/visual_tool_scale.cpp index bffd73cd7..6db9975db 100644 --- a/aegisub/src/visual_tool_scale.cpp +++ b/aegisub/src/visual_tool_scale.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -36,126 +23,95 @@ #include "config.h" #ifndef AGI_PRE -#include +#include #endif -#include "ass_dialogue.h" -#include "ass_file.h" -#include "include/aegisub/context.h" -#include "utils.h" -#include "video_context.h" -#include "video_display.h" #include "visual_tool_scale.h" -VisualToolScale::VisualToolScale(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *) -: VisualTool(parent, context, video) -, curScaleX(0.f) -, origScaleX(0.f) -, curScaleY(0.f) -, origScaleY(0.f) -, startX(0) -, startY(0) +#include "utils.h" + +VisualToolScale::VisualToolScale(VideoDisplay *parent, agi::Context *context) +: VisualTool(parent, context) { - DoRefresh(); } void VisualToolScale::Draw() { - if (!curDiag) return; + if (!active_line) return; - int len = 160; - int dx = mid(len/2+10,posx,video.w-len/2-30); - int dy = mid(len/2+10,posy,video.h-len/2-30); + // The length in pixels of the 100% zoom + static const int base_len = 160; + // The width of the y scale guide/height of the x scale guide + static const int guide_size = 10; - SetLineColour(colour[0]); - SetFillColour(colour[1],0.3f); + // Ensure that the scaling UI is comfortably visible on screen + Vector2D base_point = pos + .Max(Vector2D(base_len / 2 + guide_size, base_len / 2 + guide_size)) + .Min(video_res - base_len / 2 - guide_size * 3); - // Transform grid - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glTranslatef(dx,dy,0.f); - float matrix[16] = { 2500, 0, 0, 0, 0, 2500, 0, 0, 0, 0, 1, 1, 0, 0, 2500, 2500 }; - glMultMatrixf(matrix); - glScalef(1.f,1.f,8.f); - if (ry != 0.f) glRotatef(ry,0.f,-1.f,0.f); - if (rx != 0.f) glRotatef(rx,-1.f,0.f,0.f); - if (rz != 0.f) glRotatef(rz,0.f,0.f,-1.f); - - // Scale parameters - int lenx = int(1.6 * curScaleX); - int leny = int(1.6 * curScaleY); - int drawX = len/2 + 10; - int drawY = len/2 + 10; + // Set the origin to the base point and apply the line's rotation + gl.SetOrigin(base_point); + gl.SetRotation(rx, ry, rz); - // Draw length markers - SetLineColour(colour[3],1.f,2); - DrawLine(-lenx/2,drawY+10,lenx/2,drawY+10); - DrawLine(drawX+10,-leny/2,drawX+10,leny/2); - SetLineColour(colour[0],1.f,1); - SetFillColour(colour[1],0.3f); - DrawCircle(lenx/2,drawY+10,4); - DrawCircle(drawX+10,-leny/2,4); + Vector2D scale_half_length = scale * base_len / 200; + float minor_dim_offset = base_len / 2 + guide_size * 1.5f; - // Draw horizontal scale - SetLineColour(colour[0],1.f,1); - DrawRectangle(-len/2,drawY,len/2+1,drawY+5); - SetLineColour(colour[0],1.f,2); - DrawLine(-len/2+1,drawY+5,-len/2+1,drawY+15); - DrawLine(len/2,drawY+5,len/2,drawY+15); + // The ends of the current scale amount lines + Vector2D x_p1(minor_dim_offset, -scale_half_length.Y()); + Vector2D x_p2(minor_dim_offset, scale_half_length.Y()); + Vector2D y_p1(-scale_half_length.X(), minor_dim_offset); + Vector2D y_p2(scale_half_length.X(), minor_dim_offset); - // Draw vertical scale - SetLineColour(colour[0],1.f,1); - DrawRectangle(drawX,-len/2,drawX+5,len/2+1); - SetLineColour(colour[0],1.f,2); - DrawLine(drawX+5,-len/2+1,drawX+15,-len/2+1); - DrawLine(drawX+5,len/2,drawX+15,len/2); + // Current scale amount lines + gl.SetLineColour(colour[3], 1.f, 2); + gl.DrawLine(x_p1, x_p2); + gl.DrawLine(y_p1, y_p2); - glPopMatrix(); + // Fake features at the end of the lines + gl.SetLineColour(colour[0], 1.f, 1); + gl.SetFillColour(colour[1], 0.3f); + gl.DrawCircle(x_p1, 4); + gl.DrawCircle(x_p2, 4); + gl.DrawCircle(y_p1, 4); + gl.DrawCircle(y_p2, 4); + + // Draw the guides + int half_len = base_len / 2; + gl.SetLineColour(colour[0], 1.f, 1); + gl.DrawRectangle(Vector2D(half_len, -half_len), Vector2D(half_len + guide_size, half_len)); + gl.DrawRectangle(Vector2D(-half_len, half_len), Vector2D(half_len, half_len + guide_size)); + + // Draw the feet + gl.SetLineColour(colour[0], 1.f, 2); + gl.DrawLine(Vector2D(half_len + guide_size, -half_len), Vector2D(half_len + guide_size + guide_size / 2, -half_len)); + gl.DrawLine(Vector2D(half_len + guide_size, half_len), Vector2D(half_len + guide_size + guide_size / 2, half_len)); + gl.DrawLine(Vector2D(-half_len, half_len + guide_size), Vector2D(-half_len, half_len + guide_size + guide_size / 2)); + gl.DrawLine(Vector2D(half_len, half_len + guide_size), Vector2D(half_len, half_len + guide_size + guide_size / 2)); + + gl.ResetTransform(); } bool VisualToolScale::InitializeHold() { - startX = video.x; - startY = video.y; - origScaleX = curScaleX; - origScaleY = curScaleY; - curDiag->StripTag("\\fscx"); - curDiag->StripTag("\\fscy"); - + initial_scale = scale; return true; } void VisualToolScale::UpdateHold() { - // Deltas - int deltaX = video.x - startX; - int deltaY = startY - video.y; - if (shiftDown) { - if (abs(deltaX) >= abs(deltaY)) deltaY = 0; - else deltaX = 0; - } + Vector2D delta = mouse_pos - drag_start; + if (shift_down) + delta = delta.SingleAxis(); - // Calculate - curScaleX = std::max(deltaX*1.25f + origScaleX, 0.f); - curScaleY = std::max(deltaY*1.25f + origScaleY, 0.f); + scale = Vector2D().Max(delta * 1.25f + initial_scale); + if (ctrl_down) + scale = scale.Round(25.f); - // Oh Snap - if (ctrlDown) { - curScaleX = floorf(curScaleX/25.f+.5f)*25.f; - curScaleY = floorf(curScaleY/25.f+.5f)*25.f; - } -} - -void VisualToolScale::CommitHold() { - Selection sel = c->selectionController->GetSelectedSet(); - for (Selection::const_iterator cur = sel.begin(); cur != sel.end(); ++cur) { - SetOverride(*cur, "\\fscx",wxString::Format("(%0.3g)",curScaleX)); - SetOverride(*cur, "\\fscy",wxString::Format("(%0.3g)",curScaleY)); - } + SetSelectedOverride("\\fscx", wxString::Format("(%0.3g)", scale.X())); + SetSelectedOverride("\\fscy", wxString::Format("(%0.3g)", scale.Y())); } void VisualToolScale::DoRefresh() { - if (!curDiag) return; + if (!active_line) return; - GetLineScale(curDiag, curScaleX, curScaleY); - GetLinePosition(curDiag, posx, posy); - GetLineRotation(curDiag, rx, ry, rz); + GetLineScale(active_line, scale); + GetLineRotation(active_line, rx, ry, rz); + pos = FromScriptCoords(GetLinePosition(active_line)); } diff --git a/aegisub/src/visual_tool_scale.h b/aegisub/src/visual_tool_scale.h index ae1b2cf16..89da48b46 100644 --- a/aegisub/src/visual_tool_scale.h +++ b/aegisub/src/visual_tool_scale.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -41,20 +28,19 @@ /// @class VisualToolScale /// @brief DOCME class VisualToolScale : public VisualTool { - float curScaleX, origScaleX; - float curScaleY, origScaleY; - - int startX, startY; - int posx, posy; - float rx, ry, rz; + Vector2D scale; ///< The current scale + Vector2D initial_scale; ///< The scale at the beginning of the current hold + Vector2D pos; ///< Position of the line + float rx; ///< X rotation + float ry; ///< Y rotation + float rz; ///< Z rotation bool InitializeHold(); void UpdateHold(); - void CommitHold(); void DoRefresh(); void Draw(); public: - VisualToolScale(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *); + VisualToolScale(VideoDisplay *parent, agi::Context *context); }; diff --git a/aegisub/src/visual_tool_vector_clip.cpp b/aegisub/src/visual_tool_vector_clip.cpp index 5e6e36da5..a9bde7b59 100644 --- a/aegisub/src/visual_tool_vector_clip.cpp +++ b/aegisub/src/visual_tool_vector_clip.cpp @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -38,30 +25,18 @@ #ifndef AGI_PRE #include -#ifdef HAVE_APPLE_OPENGL_FRAMEWORK -#include -#else -#include "gl/glext.h" -#endif - #include #endif -#ifdef __APPLE__ -/// Noop macro, this should never be defined in a header. -#define APIENTRY -#endif - #include "config.h" #include "ass_dialogue.h" #include "libresrc/libresrc.h" #include "utils.h" -#include "video_display.h" /// Button IDs enum { - BUTTON_DRAG = VISUAL_SUB_TOOL_START, + BUTTON_DRAG = 1300, BUTTON_LINE, BUTTON_BICUBIC, BUTTON_CONVERT, @@ -72,34 +47,28 @@ enum { BUTTON_LAST // Leave this at the end and don't use it }; -template -static void for_each_iter(C &container, O obj, M method) { - typename C::iterator end = container.end(); - for (typename C::iterator cur = container.begin(); cur != end; ++cur) { - (obj ->* method)(cur); - } +VisualToolVectorClip::VisualToolVectorClip(VideoDisplay *parent, agi::Context *context) +: VisualTool(parent, context) +, spline(*this) +{ } -VisualToolVectorClip::VisualToolVectorClip(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar * toolBar) -: VisualTool(parent, context, video) -, spline(*parent) -, toolBar(toolBar) -{ - toolBar->AddTool(BUTTON_DRAG,_("Drag"),GETIMAGE(visual_vector_clip_drag_24),_("Drag control points."),wxITEM_CHECK); - toolBar->AddTool(BUTTON_LINE,_("Line"),GETIMAGE(visual_vector_clip_line_24),_("Appends a line."),wxITEM_CHECK); - toolBar->AddTool(BUTTON_BICUBIC,_("Bicubic"),GETIMAGE(visual_vector_clip_bicubic_24),_("Appends a bezier bicubic curve."),wxITEM_CHECK); +void VisualToolVectorClip::SetToolbar(wxToolBar *toolBar) { + this->toolBar = toolBar; + toolBar->AddTool(BUTTON_DRAG, _("Drag"), GETIMAGE(visual_vector_clip_drag_24), _("Drag control points."), wxITEM_CHECK); + toolBar->AddTool(BUTTON_LINE, _("Line"), GETIMAGE(visual_vector_clip_line_24), _("Appends a line."), wxITEM_CHECK); + toolBar->AddTool(BUTTON_BICUBIC, _("Bicubic"), GETIMAGE(visual_vector_clip_bicubic_24), _("Appends a bezier bicubic curve."), wxITEM_CHECK); toolBar->AddSeparator(); - toolBar->AddTool(BUTTON_CONVERT,_("Convert"),GETIMAGE(visual_vector_clip_convert_24),_("Converts a segment between line and bicubic."),wxITEM_CHECK); - toolBar->AddTool(BUTTON_INSERT,_("Insert"),GETIMAGE(visual_vector_clip_insert_24),_("Inserts a control point."),wxITEM_CHECK); - toolBar->AddTool(BUTTON_REMOVE,_("Remove"),GETIMAGE(visual_vector_clip_remove_24),_("Removes a control point."),wxITEM_CHECK); + toolBar->AddTool(BUTTON_CONVERT, _("Convert"), GETIMAGE(visual_vector_clip_convert_24), _("Converts a segment between line and bicubic."), wxITEM_CHECK); + toolBar->AddTool(BUTTON_INSERT, _("Insert"), GETIMAGE(visual_vector_clip_insert_24), _("Inserts a control point."), wxITEM_CHECK); + toolBar->AddTool(BUTTON_REMOVE, _("Remove"), GETIMAGE(visual_vector_clip_remove_24), _("Removes a control point."), wxITEM_CHECK); toolBar->AddSeparator(); - toolBar->AddTool(BUTTON_FREEHAND,_("Freehand"),GETIMAGE(visual_vector_clip_freehand_24),_("Draws a freehand shape."),wxITEM_CHECK); - toolBar->AddTool(BUTTON_FREEHAND_SMOOTH,_("Freehand smooth"),GETIMAGE(visual_vector_clip_freehand_smooth_24),_("Draws a smoothed freehand shape."),wxITEM_CHECK); - toolBar->ToggleTool(BUTTON_DRAG,true); + toolBar->AddTool(BUTTON_FREEHAND, _("Freehand"), GETIMAGE(visual_vector_clip_freehand_24), _("Draws a freehand shape."), wxITEM_CHECK); + toolBar->AddTool(BUTTON_FREEHAND_SMOOTH, _("Freehand smooth"), GETIMAGE(visual_vector_clip_freehand_smooth_24), _("Draws a smoothed freehand shape."), wxITEM_CHECK); + toolBar->ToggleTool(BUTTON_DRAG, true); toolBar->Realize(); toolBar->Show(true); - - DoRefresh(); + toolBar->Bind(wxEVT_COMMAND_TOOL_CLICKED, &VisualToolVectorClip::OnSubTool, this); SetMode(features.empty()); } @@ -107,35 +76,23 @@ void VisualToolVectorClip::OnSubTool(wxCommandEvent &event) { SetMode(event.GetId() - BUTTON_DRAG); } -void VisualToolVectorClip::SetMode(int newMode) { +void VisualToolVectorClip::SetMode(int new_mode) { // Manually enforce radio behavior as we want one selection in the bar // rather than one per group - for (int i=BUTTON_DRAG;iToggleTool(i,i == newMode + BUTTON_DRAG); - } - mode = newMode; -} + for (int i = BUTTON_DRAG; i < BUTTON_LAST; i++) + toolBar->ToggleTool(i, i == new_mode + BUTTON_DRAG); -// Substitute for glMultiDrawArrays for sub-1.4 OpenGL -// Not required on OS X. -#ifndef __APPLE__ -static void APIENTRY glMultiDrawArraysFallback(GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount) { - for (int i = 0; i < primcount; ++i) { - glDrawArrays(mode, *first++, *count++); - } + mode = new_mode; } -#endif static bool is_move(SplineCurve const& c) { - return c.type == CURVE_POINT; + return c.type == SplineCurve::POINT; } void VisualToolVectorClip::Draw() { - if (!curDiag) return; + if (!active_line) return; if (spline.empty()) return; - GL_EXT(PFNGLMULTIDRAWARRAYSPROC, glMultiDrawArrays); - // Parse vector std::vector points; std::vector start; @@ -144,78 +101,33 @@ void VisualToolVectorClip::Draw() { spline.GetPointList(points, start, count); assert(!start.empty()); assert(!count.empty()); - - glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(2, GL_FLOAT, 0, &points[0]); - // The following is nonzero winding-number PIP based on stencils + gl.SetLineColour(colour[3], 1.f, 2); + gl.SetFillColour(wxColour(0, 0, 0), 0.5f); - // Draw to stencil only - glEnable(GL_STENCIL_TEST); - glColorMask(0, 0, 0, 0); - - // GL_INCR_WRAP was added in 1.4, so instead set the entire stencil to 128 - // and wobble from there - glStencilFunc(GL_NEVER, 128, 0xFF); - glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); - DrawRectangle(0,0,video.w,video.h); - - // Increment the winding number for each forward facing triangle - glStencilOp(GL_INCR, GL_INCR, GL_INCR); - glEnable(GL_CULL_FACE); - - glCullFace(GL_BACK); - glMultiDrawArrays(GL_TRIANGLE_FAN, &start[0], &count[0], start.size()); - - // Decrement the winding number for each backfacing triangle - glStencilOp(GL_DECR, GL_DECR, GL_DECR); - glCullFace(GL_FRONT); - glMultiDrawArrays(GL_TRIANGLE_FAN, &start[0], &count[0], start.size()); - glDisable(GL_CULL_FACE); - - // Draw the actual rectangle - glColorMask(1,1,1,1); - SetLineColour(colour[3],0.f); - SetFillColour(wxColour(0,0,0),0.5f); - - // VSFilter draws when the winding number is nonzero, so we want to draw the - // mask when the winding number is zero (where 128 is zero due to the lack of - // wrapping combined with unsigned numbers) - glStencilFunc(inverse ? GL_NOTEQUAL : GL_EQUAL, 128, 0xFF); - DrawRectangle(0,0,video.w,video.h); - glDisable(GL_STENCIL_TEST); - - // Draw lines - SetFillColour(colour[3],0.f); - SetLineColour(colour[3],1.f,2); - SetModeLine(); - glMultiDrawArrays(GL_LINE_LOOP, &start[0], &count[0], start.size()); + gl.DrawMultiPolygon(points, start, count, video_res, !inverse); Vector2D pt; float t; - Spline::iterator highCurve; - spline.GetClosestParametricPoint(Vector2D(video.x, video.y), highCurve, t, pt); + Spline::iterator highlighted_curve; + spline.GetClosestParametricPoint(mouse_pos, highlighted_curve, t, pt); // Draw highlighted line - if ((mode == 3 || mode == 4) && curFeature == features.end() && points.size() > 2) { - std::vector highPoints; - spline.GetPointList(highPoints, highCurve); - if (!highPoints.empty()) { - glVertexPointer(2, GL_FLOAT, 0, &highPoints[0]); - SetLineColour(colour[2], 1.f, 2); - SetModeLine(); - glDrawArrays(GL_LINE_STRIP, 0, highPoints.size() / 2); + if ((mode == 3 || mode == 4) && active_feature == features.end() && points.size() > 2) { + std::vector highlighted_points; + spline.GetPointList(highlighted_points, highlighted_curve); + if (!highlighted_points.empty()) { + gl.SetLineColour(colour[2], 1.f, 2); + gl.DrawLineStrip(2, highlighted_points); } } - glDisableClientState(GL_VERTEX_ARRAY); - // Draw lines connecting the bicubic features - SetLineColour(colour[3],0.9f,1); - for (Spline::iterator cur=spline.begin();cur!=spline.end();cur++) { - if (cur->type == CURVE_BICUBIC) { - DrawDashedLine(cur->p1.x,cur->p1.y,cur->p2.x,cur->p2.y,6); - DrawDashedLine(cur->p3.x,cur->p3.y,cur->p4.x,cur->p4.y,6); + gl.SetLineColour(colour[3], 0.9f, 1); + for (Spline::iterator cur = spline.begin(); cur != spline.end(); ++cur) { + if (cur->type == SplineCurve::BICUBIC) { + gl.DrawDashedLine(cur->p1, cur->p2, 6); + gl.DrawDashedLine(cur->p3, cur->p4, 6); } } @@ -223,94 +135,84 @@ void VisualToolVectorClip::Draw() { // Draw preview of inserted line if (mode == 1 || mode == 2) { - if (spline.size() && video.x > INT_MIN && video.y > INT_MIN) { + if (spline.size() && mouse_pos) { Spline::reverse_iterator c0 = std::find_if(spline.rbegin(), spline.rend(), is_move); SplineCurve *c1 = &spline.back(); - DrawDashedLine(video.x,video.y,c0->p1.x,c0->p1.y,6); - DrawDashedLine(video.x,video.y,c1->EndPoint().x,c1->EndPoint().y,6); + gl.DrawDashedLine(mouse_pos, c0->p1, 6); + gl.DrawDashedLine(mouse_pos, c1->EndPoint(), 6); } } // Draw preview of insert point - if (mode == 4) DrawCircle(pt.x,pt.y,4); + if (mode == 4) + gl.DrawCircle(pt, 4); } void VisualToolVectorClip::MakeFeature(Spline::iterator cur) { Feature feat; - if (cur->type == CURVE_POINT) { - feat.x = (int)cur->p1.x; - feat.y = (int)cur->p1.y; + feat.curve = cur; + + if (cur->type == SplineCurve::POINT) { + feat.pos = cur->p1; feat.type = DRAG_SMALL_CIRCLE; - feat.curve = cur; feat.point = 0; - features.push_back(feat); } - - else if (cur->type == CURVE_LINE) { - feat.x = (int)cur->p2.x; - feat.y = (int)cur->p2.y; + else if (cur->type == SplineCurve::LINE) { + feat.pos = cur->p2; feat.type = DRAG_SMALL_CIRCLE; - feat.curve = cur; feat.point = 1; - features.push_back(feat); } - - else if (cur->type == CURVE_BICUBIC) { + else if (cur->type == SplineCurve::BICUBIC) { // Control points - feat.x = (int)cur->p2.x; - feat.y = (int)cur->p2.y; - feat.curve = cur; + feat.pos = cur->p2; feat.point = 1; feat.type = DRAG_SMALL_SQUARE; features.push_back(feat); - feat.x = (int)cur->p3.x; - feat.y = (int)cur->p3.y; + feat.pos = cur->p3; feat.point = 2; features.push_back(feat); // End point - feat.x = (int)cur->p4.x; - feat.y = (int)cur->p4.y; + feat.pos = cur->p4; feat.type = DRAG_SMALL_CIRCLE; feat.point = 3; - features.push_back(feat); } + features.push_back(feat); } void VisualToolVectorClip::MakeFeatures() { - ClearSelection(); + sel_features.clear(); features.clear(); - for_each_iter(spline, this, &VisualToolVectorClip::MakeFeature); + active_feature = features.end(); + for (Spline::iterator it = spline.begin(); it != spline.end(); ++it) + MakeFeature(it); } void VisualToolVectorClip::Save() { - SetOverride(curDiag, inverse ? "\\iclip" : "\\clip", "(" + spline.EncodeToASS() + ")"); + SetOverride(active_line, inverse ? "\\iclip" : "\\clip", "(" + spline.EncodeToASS() + ")"); } void VisualToolVectorClip::UpdateDrag(feature_iterator feature) { - spline.MovePoint(feature->curve,feature->point,Vector2D(feature->x,feature->y)); -} - -void VisualToolVectorClip::CommitDrag(feature_iterator) { + spline.MovePoint(feature->curve, feature->point, feature->pos); Save(); } bool VisualToolVectorClip::InitializeDrag(feature_iterator feature) { if (mode != 5) return true; - if (feature->curve->type == CURVE_BICUBIC && (feature->point == 1 || feature->point == 2)) { + if (feature->curve->type == SplineCurve::BICUBIC && (feature->point == 1 || feature->point == 2)) { // Deleting bicubic curve handles, so convert to line - feature->curve->type = CURVE_LINE; + feature->curve->type = SplineCurve::LINE; feature->curve->p2 = feature->curve->p4; } else { Spline::iterator next = feature->curve; next++; if (next != spline.end()) { - if (feature->curve->type == CURVE_POINT) { + if (feature->curve->type == SplineCurve::POINT) { next->p1 = next->EndPoint(); - next->type = CURVE_POINT; + next->type = SplineCurve::POINT; } else { next->p1 = feature->curve->p1; @@ -319,7 +221,7 @@ bool VisualToolVectorClip::InitializeDrag(feature_iterator feature) { spline.erase(feature->curve); } - curFeature = features.end(); + active_feature = features.end(); Save(); MakeFeatures(); @@ -333,21 +235,20 @@ bool VisualToolVectorClip::InitializeHold() { if (mode == 1 || mode == 2) { SplineCurve curve; - // Set start position - if (!spline.empty()) { - curve.p1 = spline.back().EndPoint(); - curve.type = mode == 1 ? CURVE_LINE : CURVE_BICUBIC; + // New spline beginning at the clicked point + if (spline.empty()) { + curve.p1 = mouse_pos; + curve.type = SplineCurve::POINT; } - - // First point else { - curve.p1 = Vector2D(video.x,video.y); - curve.type = CURVE_POINT; + // Continue from the spline in progress + // Don't bother setting p2 as UpdateHold will handle that + curve.p1 = spline.back().EndPoint(); + curve.type = mode == 1 ? SplineCurve::LINE : SplineCurve::BICUBIC; } - // Insert spline.push_back(curve); - ClearSelection(); + sel_features.clear(); MakeFeature(--spline.end()); UpdateHold(); return true; @@ -359,93 +260,85 @@ bool VisualToolVectorClip::InitializeHold() { Vector2D pt; Spline::iterator curve; float t; - spline.GetClosestParametricPoint(Vector2D(video.x,video.y),curve,t,pt); + spline.GetClosestParametricPoint(mouse_pos, curve, t, pt); - // Convert + // Convert line <-> bicubic if (mode == 3) { if (curve != spline.end()) { - if (curve->type == CURVE_LINE) { - curve->type = CURVE_BICUBIC; + if (curve->type == SplineCurve::LINE) { + curve->type = SplineCurve::BICUBIC; curve->p4 = curve->p2; curve->p2 = curve->p1 * 0.75 + curve->p4 * 0.25; curve->p3 = curve->p1 * 0.25 + curve->p4 * 0.75; } - else if (curve->type == CURVE_BICUBIC) { - curve->type = CURVE_LINE; + else if (curve->type == SplineCurve::BICUBIC) { + curve->type = SplineCurve::LINE; curve->p2 = curve->p4; } } } - // Insert else { - // Check if there is at least one curve to split if (spline.empty()) return false; // Split the curve if (curve == spline.end()) { - SplineCurve ct; - ct.type = CURVE_LINE; - ct.p1 = spline.back().EndPoint(); - ct.p2 = spline.front().p1; - ct.p2 = ct.p1*(1-t) + ct.p2*t; + SplineCurve ct(spline.back().EndPoint(), spline.front().p1); + ct.p2 = ct.p1 * (1 - t) + ct.p2 * t; spline.push_back(ct); } else { SplineCurve c2; - curve->Split(*curve,c2,t); + curve->Split(*curve, c2, t); spline.insert(++curve, c2); } } - // Commit Save(); MakeFeatures(); Commit(); return false; } - // Freehand + // Freehand spline draw if (mode == 6 || mode == 7) { - ClearSelection(); + sel_features.clear(); features.clear(); + active_feature = features.end(); spline.clear(); - SplineCurve curve; - curve.type = CURVE_POINT; - curve.p1.x = video.x; - curve.p1.y = video.y; - spline.push_back(curve); + spline.push_back(SplineCurve(mouse_pos)); return true; } + + /// @todo box selection? + if (mode == 0) { + return false; + } + + // Nothing to do for mode 5 (remove) return false; } void VisualToolVectorClip::UpdateHold() { - // Insert line if (mode == 1) { - spline.back().EndPoint() = Vector2D(video.x,video.y); - features.back().x = video.x; - features.back().y = video.y; + spline.back().EndPoint() = mouse_pos; + features.back().pos = mouse_pos; } // Insert bicubic else if (mode == 2) { SplineCurve &curve = spline.back(); - curve.EndPoint() = Vector2D(video.x,video.y); + curve.EndPoint() = mouse_pos; // Control points if (spline.size() > 1) { - std::list::reverse_iterator iter = spline.rbegin(); - iter++; - SplineCurve &c0 = *iter; - Vector2D prevVector; - float len = (curve.p4-curve.p1).Len(); - if (c0.type == CURVE_LINE) prevVector = c0.p2-c0.p1; - else prevVector = c0.p4-c0.p3; - curve.p2 = prevVector.Unit() * (0.25f*len) + curve.p1; + SplineCurve &c0 = spline.back(); + float len = (curve.p4 - curve.p1).Len(); + curve.p2 = (c0.type == SplineCurve::LINE ? c0.p2 - c0.p1 : c0.p4 - c0.p3).Unit() * (0.25f * len) + curve.p1; } - else curve.p2 = curve.p1 * 0.75 + curve.p4 * 0.25; + else + curve.p2 = curve.p1 * 0.75 + curve.p4 * 0.25; curve.p3 = curve.p1 * 0.25 + curve.p4 * 0.75; MakeFeatures(); } @@ -454,27 +347,19 @@ void VisualToolVectorClip::UpdateHold() { else if (mode == 6 || mode == 7) { // See if distance is enough Vector2D const& last = spline.back().EndPoint(); - int len = (int)Vector2D(last.x-video.x, last.y-video.y).Len(); - if (mode == 6 && len < 30) return; - if (mode == 7 && len < 60) return; + float len = (last - mouse_pos).SquareLen(); + if (mode == 6 && len < 900) return; + if (mode == 7 && len < 3600) return; - // Generate curve and add it - SplineCurve curve; - curve.type = CURVE_LINE; - curve.p1 = Vector2D(last.x,last.y); - curve.p2 = Vector2D(video.x,video.y); - spline.push_back(curve); + spline.push_back(SplineCurve(last, mouse_pos)); MakeFeature(--spline.end()); } -} -void VisualToolVectorClip::CommitHold() { if (mode == 3 || mode == 4) return; // Smooth spline - if (!holding && mode == 7) { + if (!holding && mode == 7) spline.Smooth(); - } Save(); @@ -486,11 +371,11 @@ void VisualToolVectorClip::CommitHold() { } void VisualToolVectorClip::DoRefresh() { - if (!curDiag) return; + if (!active_line) return; wxString vect; int scale; - vect = GetLineVectorClip(curDiag,scale,inverse); + vect = GetLineVectorClip(active_line, scale, inverse); spline.DecodeFromASS(vect); MakeFeatures(); @@ -498,6 +383,7 @@ void VisualToolVectorClip::DoRefresh() { } void VisualToolVectorClip::SelectAll() { - ClearSelection(); - for_each_iter(features, this, &VisualToolVectorClip::AddSelection); + sel_features.clear(); + for (feature_iterator it = features.begin(); it != features.end(); ++it) + sel_features.insert(it); } diff --git a/aegisub/src/visual_tool_vector_clip.h b/aegisub/src/visual_tool_vector_clip.h index cb2d13265..0d21ca20d 100644 --- a/aegisub/src/visual_tool_vector_clip.h +++ b/aegisub/src/visual_tool_vector_clip.h @@ -1,29 +1,16 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2011, Thomas Goyne // -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: +// 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. // -// * 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. +// 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/ // @@ -43,8 +30,7 @@ class wxToolBar; /// @class VisualToolVectorClipDraggableFeature /// @brief VisualDraggableFeature with information about a feature's location /// in the spline -class VisualToolVectorClipDraggableFeature : public VisualDraggableFeature { -public: +struct VisualToolVectorClipDraggableFeature : public VisualDraggableFeature { /// Which curve in the spline this feature is a point on Spline::iterator curve; /// 0-3; indicates which part of the curve this point is @@ -77,17 +63,15 @@ class VisualToolVectorClip : public VisualTool= 1 && mode <= 4; } public: - VisualToolVectorClip(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *toolbar); + VisualToolVectorClip(VideoDisplay *parent, agi::Context *context); + void SetToolbar(wxToolBar *tb); /// Subtoolbar button click handler void OnSubTool(wxCommandEvent &event);