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);