Mostly rewrite the visual tools and related classes

Convert all coordinates within the visual tools to Vector2D, which has
been significantly extended. Eliminates a lot of issues with accumulated
rounding errors and simplifies a lot of code.

Modernize the visual tools' interactions with the rest of Aegisub by
connecting to signals directly rather than routing everything through
the video display and converting the main visual tool mode toolbar to
the command system.

Extract all references to OpenGL from the visual tools and move them to
OpenGLWrapper as a first step towards making it possible to implement an
alternative video renderer. In the process, eliminate all uses of OpenGL
immediate mode.

Fix a bunch of minor issues and general instability.

Originally committed to SVN as r5823.
This commit is contained in:
Thomas Goyne 2011-11-06 17:18:20 +00:00
parent 2e5cbf079e
commit be77dc8307
51 changed files with 2440 additions and 3195 deletions

View file

@ -1919,6 +1919,10 @@
RelativePath="..\..\src\command\video.cpp" RelativePath="..\..\src\command\video.cpp"
> >
</File> </File>
<File
RelativePath="..\..\src\command\vis_tool.cpp"
>
</File>
</Filter> </Filter>
<Filter <Filter
Name="Preferences" Name="Preferences"

View file

@ -288,6 +288,7 @@
<ClCompile Include="$(SrcDir)command\timecode.cpp" /> <ClCompile Include="$(SrcDir)command\timecode.cpp" />
<ClCompile Include="$(SrcDir)command\tool.cpp" /> <ClCompile Include="$(SrcDir)command\tool.cpp" />
<ClCompile Include="$(SrcDir)command\video.cpp" /> <ClCompile Include="$(SrcDir)command\video.cpp" />
<ClCompile Include="$(SrcDir)command\vis_tool.cpp" />
<ClCompile Include="$(SrcDir)compat.cpp" /> <ClCompile Include="$(SrcDir)compat.cpp" />
<ClCompile Include="$(SrcDir)dialog_about.cpp" /> <ClCompile Include="$(SrcDir)dialog_about.cpp" />
<ClCompile Include="$(SrcDir)dialog_attachments.cpp" /> <ClCompile Include="$(SrcDir)dialog_attachments.cpp" />

View file

@ -1196,6 +1196,9 @@
<ClCompile Include="$(SrcDir)command\video.cpp"> <ClCompile Include="$(SrcDir)command\video.cpp">
<Filter>Commands</Filter> <Filter>Commands</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="$(SrcDir)command\vis_tool.cpp">
<Filter>Commands</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)pen.cpp"> <ClCompile Include="$(SrcDir)pen.cpp">
<Filter>Utilities\UI utilities</Filter> <Filter>Utilities\UI utilities</Filter>
</ClCompile> </ClCompile>

View file

@ -160,6 +160,7 @@
#include <wx/msgdlg.h> #include <wx/msgdlg.h>
#include <wx/mstream.h> #include <wx/mstream.h>
#include <wx/notebook.h> #include <wx/notebook.h>
#include <wx/numformatter.h>
#include <wx/panel.h> #include <wx/panel.h>
#include <wx/power.h> #include <wx/power.h>
#include <wx/protocol/http.h> #include <wx/protocol/http.h>
@ -170,11 +171,11 @@
#include <wx/regex.h> #include <wx/regex.h>
#include <wx/sashwin.h> #include <wx/sashwin.h>
#include <wx/scrolbar.h> #include <wx/scrolbar.h>
#include <wx/srchctrl.h>
#include <wx/settings.h> #include <wx/settings.h>
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/slider.h> #include <wx/slider.h>
#include <wx/spinctrl.h> #include <wx/spinctrl.h>
#include <wx/srchctrl.h>
#include <wx/stackwalk.h> #include <wx/stackwalk.h>
#include <wx/statbmp.h> #include <wx/statbmp.h>
#include <wx/statbox.h> #include <wx/statbox.h>

View file

@ -121,6 +121,8 @@ protected:
void AdjustScrollbar(); void AdjustScrollbar();
void SetColumnWidths(); void SetColumnWidths();
bool IsDisplayed(const AssDialogue *line) const;
// Re-implement functions from BaseSelectionController to add batching // Re-implement functions from BaseSelectionController to add batching
void AnnounceActiveLineChanged(AssDialogue *new_line); void AnnounceActiveLineChanged(AssDialogue *new_line);
void AnnounceSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed); void AnnounceSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed);
@ -139,7 +141,6 @@ public:
void EndBatch(); void EndBatch();
void SetByFrame(bool state); void SetByFrame(bool state);
bool IsDisplayed(const AssDialogue *line) const;
void SelectRow(int row, bool addToSelected = false, bool select=true); void SelectRow(int row, bool addToSelected = false, bool select=true);
int GetFirstSelRow() const; int GetFirstSelRow() const;
wxArrayInt GetSelection() const; wxArrayInt GetSelection() const;

View file

@ -21,7 +21,8 @@ SRC = \
time.cpp \ time.cpp \
timecode.cpp \ timecode.cpp \
tool.cpp \ tool.cpp \
video.cpp video.cpp \
vis_tool.cpp
SRC += \ SRC += \
icon.cpp \ icon.cpp \

View file

@ -88,6 +88,7 @@ namespace cmd {
void init_timecode(); void init_timecode();
void init_tool(); void init_tool();
void init_video(); void init_video();
void init_visual_tools();
void init_builtin_commands() { void init_builtin_commands() {
LOG_D("command/init") << "Populating command map"; LOG_D("command/init") << "Populating command map";
@ -104,6 +105,7 @@ namespace cmd {
init_timecode(); init_timecode();
init_tool(); init_tool();
init_video(); init_video();
init_visual_tools();
} }
void clear() { void clear() {

View file

@ -198,6 +198,13 @@ INSERT_ICON("video/opt/autoscroll", toggle_video_autoscroll)
INSERT_ICON("video/play", button_play) INSERT_ICON("video/play", button_play)
INSERT_ICON("video/play/line", button_playline) INSERT_ICON("video/play/line", button_playline)
INSERT_ICON("video/stop", button_pause) 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/in", zoom_in_button)
INSERT_ICON("video/zoom/out", zoom_out_button) INSERT_ICON("video/zoom/out", zoom_out_button)

View file

@ -238,10 +238,7 @@ struct video_copy_coordinates : public validator_video_loaded {
void operator()(agi::Context *c) { void operator()(agi::Context *c) {
if (wxTheClipboard->Open()) { if (wxTheClipboard->Open()) {
int x, y; wxTheClipboard->SetData(new wxTextDataObject(c->videoBox->videoDisplay->GetMousePosition().Str()));
c->videoBox->videoDisplay->GetMousePosition(&x, &y);
c->videoBox->videoDisplay->ToScriptCoords(&x, &y);
wxTheClipboard->SetData(new wxTextDataObject(wxString::Format("%d,%d", x, y)));
wxTheClipboard->Close(); wxTheClipboard->Close();
} }
} }

View file

@ -0,0 +1,142 @@
// Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
//
// $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);
}
}

View file

@ -52,7 +52,6 @@
#include "utils.h" #include "utils.h"
#include "validators.h" #include "validators.h"
#include "video_context.h" #include "video_context.h"
#include "video_display.h"
#include "video_provider_manager.h" #include "video_provider_manager.h"
DialogProperties::DialogProperties(agi::Context *c) DialogProperties::DialogProperties(agi::Context *c)

View file

@ -52,7 +52,6 @@
#include "selection_controller.h" #include "selection_controller.h"
#include "subs_edit_ctrl.h" #include "subs_edit_ctrl.h"
#include "subs_grid.h" #include "subs_grid.h"
#include "video_display.h"
// IDs // IDs
enum { enum {

View file

@ -55,7 +55,6 @@
#include "utils.h" #include "utils.h"
#include "validators.h" #include "validators.h"
#include "video_context.h" #include "video_context.h"
#include "video_display.h"
/// Window IDs /// Window IDs
enum { enum {

View file

@ -342,20 +342,30 @@ void OpenGLTextTexture::Insert(OpenGLTextGlyph &glyph) {
/// Draw a glyph at (x,y) /// Draw a glyph at (x,y)
void OpenGLTextGlyph::Draw(int x,int y) const { 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); glBindTexture(GL_TEXTURE_2D, tex);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBegin(GL_QUADS); float tex_coords[] = {
glTexCoord2f(x1,y1); glVertex2f(0,0); // Top-left x1, y1,
glTexCoord2f(x1,y2); glVertex2f(0,h); // Bottom-left x1, y2,
glTexCoord2f(x2,y2); glVertex2f(w,h); // Bottom-right x2, y2,
glTexCoord2f(x2,y1); glVertex2f(w,0); // Top-right x2, y1
glEnd(); };
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 /// @brief DOCME

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -42,303 +29,417 @@
#ifndef AGI_PRE #ifndef AGI_PRE
#include <wx/msgdlg.h> #include <wx/msgdlg.h>
#ifdef __APPLE__ #ifdef HAVE_APPLE_OPENGL_FRAMEWORK
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/glext.h> #include <OpenGL/glext.h>
#else #else
#include <GL/gl.h>
#include <GL/glu.h>
#include "gl/glext.h" #include "gl/glext.h"
#endif #endif
#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 <GL/glx.h>
#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<type>(glGetProc(#name)); \
if (!name) { \
name = reinterpret_cast<type>(& name ## Fallback); \
}
#endif
class VertexArray {
std::vector<float> 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() { OpenGLWrapper::OpenGLWrapper() {
r1 = g1 = b1 = a1 = 1.0f; line_r = line_g = line_b = line_a = 1.f;
r2 = g2 = b2 = a2 = 1.0f; fill_r = fill_g = fill_b = fill_a = 1.f;
lw = 1; line_width = 1;
transform_pushed = false;
smooth = true;
} }
void OpenGLWrapper::DrawLine(Vector2D p1, Vector2D p2) const {
/// @brief Draw line
/// @param x1
/// @param y1
/// @param x2
/// @param y2
///
void OpenGLWrapper::DrawLine(float x1,float y1,float x2,float y2) const {
SetModeLine(); SetModeLine();
glBegin(GL_LINES); VertexArray buf(2, 2);
glVertex2f(x1,y1); buf.Set(0, p1);
glVertex2f(x2,y2); buf.Set(1, p2);
glEnd(); buf.Draw(GL_LINES);
} }
static inline Vector2D interp(Vector2D p1, Vector2D p2, float t) {
return t * p1 + (1 - t) * p2;
}
void OpenGLWrapper::DrawDashedLine(Vector2D p1, Vector2D p2, float step) const {
/// @brief Draw line float dist = (p2 - p1).Len();
/// @param x1 step /= dist;
/// @param y1 dist -= step;
/// @param x2 for (float t = 0; t < 1.f; t += 2 * step) {
/// @param y2 DrawLine(interp(p1, p2, t), interp(p1, p2, t + step));
/// @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<steps;i++) {
if (i % 2 == 0) DrawLine(x1+int(i*stepx),y1+int(i*stepy),x1+int((i+1)*stepx),y1+int((i+1)*stepy));
} }
} }
void OpenGLWrapper::DrawEllipse(Vector2D center, Vector2D radius) const {
DrawRing(center, radius.Y(), radius.Y(), radius.X() / radius.Y());
/// @brief Draw circle
/// @param x
/// @param y
/// @param radiusX
/// @param radiusY
///
void OpenGLWrapper::DrawEllipse(float x,float y,float radiusX,float radiusY) const {
DrawRing(x,y,radiusY,radiusY,radiusX/radiusY);
} }
void OpenGLWrapper::DrawRectangle(Vector2D p1, Vector2D p2) const {
VertexArray buf(2, 4);
buf.Set(0, p1);
buf.Set(1, Vector2D(p2, p1));
buf.Set(2, p2);
buf.Set(3, Vector2D(p1, p2));
/// @brief Draw rectangle
/// @param x1
/// @param y1
/// @param x2
/// @param y2
///
void OpenGLWrapper::DrawRectangle(float x1,float y1,float x2,float y2) const {
// Fill // Fill
if (a2 != 0.0) { if (fill_a != 0.0) {
SetModeFill(); SetModeFill();
glBegin(GL_QUADS); buf.Draw(GL_QUADS, false);
glVertex2f(x1,y1);
glVertex2f(x2,y1);
glVertex2f(x2,y2);
glVertex2f(x1,y2);
glEnd();
} }
// Outline // Outline
if (a1 != 0.0) { if (line_a != 0.0) {
SetModeLine(); SetModeLine();
glBegin(GL_LINE_LOOP); buf.Draw(GL_LINE_LOOP);
glVertex2f(x1,y1);
glVertex2f(x2,y1);
glVertex2f(x2,y2);
glVertex2f(x1,y2);
glEnd();
} }
} }
void OpenGLWrapper::DrawTriangle(Vector2D p1, Vector2D p2, Vector2D p3) const {
VertexArray buf(2, 3);
buf.Set(0, p1);
buf.Set(1, p2);
buf.Set(2, p3);
/// @brief Draw triangle
/// @param x1
/// @param y1
/// @param x2
/// @param y2
/// @param x3
/// @param y3
///
void OpenGLWrapper::DrawTriangle(float x1,float y1,float x2,float y2,float x3,float y3) const {
// Fill // Fill
if (a2 != 0.0) { if (fill_a != 0.0) {
SetModeFill(); SetModeFill();
glBegin(GL_TRIANGLES); buf.Draw(GL_TRIANGLES, false);
glVertex2f(x1,y1);
glVertex2f(x2,y2);
glVertex2f(x3,y3);
glEnd();
} }
// Outline // Outline
if (a1 != 0.0) { if (line_a != 0.0) {
SetModeLine(); SetModeLine();
glBegin(GL_LINE_LOOP); buf.Draw(GL_LINE_LOOP);
glVertex2f(x1,y1);
glVertex2f(x2,y2);
glVertex2f(x3,y3);
glEnd();
} }
} }
void OpenGLWrapper::DrawRing(Vector2D center, float r1, float r2, float ar, float arc_start, float arc_end) const {
if (r2 > r1)
/// @brief Draw ring (annulus) std::swap(r1, r2);
/// @param x
/// @param y
/// @param r1
/// @param r2
/// @param ar
/// @param arcStart
/// @param arcEnd
///
void OpenGLWrapper::DrawRing(float x,float y,float r1,float r2,float ar,float arcStart,float arcEnd) const {
// Make r1 bigger
if (r2 > r1) {
float temp = r1;
r1 = r2;
r2 = temp;
}
// Arc range // Arc range
bool hasEnds = arcStart != arcEnd; bool needs_end_caps = arc_start != arc_end;
float pi = 3.1415926535897932384626433832795f;
arcEnd *= pi / 180.f; arc_end *= deg2rad;
arcStart *= pi / 180.f; arc_start *= deg2rad;
if (arcEnd <= arcStart) arcEnd += 2.0f*pi; if (arc_end <= arc_start)
float range = arcEnd - arcStart; arc_end += 2.f * pi;
float range = arc_end - arc_start;
// Math // Math
int steps = int((r1 + r1*ar) * range / (2.0f*pi))*4; int steps = std::max<int>(((r1 + r1 * ar) * range / (2.f * pi)) * 4, 12);
if (steps < 12) steps = 12; float step = range / steps;
//float end = arcEnd; float cur_angle = arc_start;
float step = range/steps;
float curAngle = arcStart;
// Fill VertexArray buf(2, steps);
if (a2 != 0.0) {
Vector2D scale_inner = Vector2D(ar, 1) * r1;
Vector2D scale_outer = Vector2D(ar, 1) * r2;
if (fill_a != 0.0) {
SetModeFill(); SetModeFill();
// Annulus // Annulus
if (r1 != r2) { if (r1 != r2) {
glBegin(GL_QUADS); buf.SetSize(2, (steps + 1) * 2);
for (int i=0;i<steps;i++) { for (int i = 0; i <= steps; i++) {
glVertex2f(x+sin(curAngle)*r1*ar,y+cos(curAngle)*r1); Vector2D offset = Vector2D::FromAngle(cur_angle);
glVertex2f(x+sin(curAngle)*r2*ar,y+cos(curAngle)*r2); buf.Set(i * 2 + 0, center + offset * scale_inner);
curAngle += step; buf.Set(i * 2 + 1, center + offset * scale_outer);
glVertex2f(x+sin(curAngle)*r2*ar,y+cos(curAngle)*r2); cur_angle += step;
glVertex2f(x+sin(curAngle)*r1*ar,y+cos(curAngle)*r1);
} }
glEnd(); buf.Draw(GL_QUAD_STRIP);
} }
// Circle // Circle
else { else {
glBegin(GL_POLYGON); buf.SetSize(2, steps);
for (int i=0;i<steps;i++) { for (int i = 0; i < steps; i++) {
glVertex2f(x+sin(curAngle)*r1,y+cos(curAngle)*r1); buf.Set(i, center + Vector2D::FromAngle(cur_angle) * scale_inner);
curAngle += step; cur_angle += step;
} }
glEnd(); buf.Draw(GL_POLYGON);
} }
// Reset angle cur_angle = arc_start;
curAngle = arcStart;
} }
// Outlines if (line_a == 0.0) return;
if (a1 != 0.0) {
// Outer
steps++;
SetModeLine();
glBegin(GL_LINE_STRIP);
for (int i=0;i<steps;i++) {
glVertex2f(x+sin(curAngle)*r1,y+cos(curAngle)*r1);
curAngle += step;
}
glEnd();
// Inner // Outer
if (r1 != r2) { steps++;
curAngle = arcStart; buf.SetSize(2, steps);
glBegin(GL_LINE_STRIP);
for (int i=0;i<steps;i++) {
glVertex2f(x+sin(curAngle)*r2,y+cos(curAngle)*r2);
curAngle += step;
}
glEnd();
// End caps SetModeLine();
if (hasEnds) { for (int i = 0; i < steps; i++) {
glBegin(GL_LINES); buf.Set(i, center + Vector2D::FromAngle(cur_angle) * scale_outer);
glVertex2f(x+sin(arcStart)*r1,y+cos(arcStart)*r1); cur_angle += step;
glVertex2f(x+sin(arcStart)*r2,y+cos(arcStart)*r2);
glVertex2f(x+sin(arcEnd)*r1,y+cos(arcEnd)*r1);
glVertex2f(x+sin(arcEnd)*r2,y+cos(arcEnd)*r2);
glEnd();
}
}
} }
buf.Draw(GL_LINE_STRIP);
// Inner
if (r1 == r2) return;
cur_angle = arc_start;
buf.SetSize(2, steps);
for (int i = 0; i < steps; i++) {
buf.Set(i, center + Vector2D::FromAngle(cur_angle) * scale_inner);
cur_angle += step;
}
buf.Draw(GL_LINE_STRIP);
if (!needs_end_caps) return;
buf.SetSize(2, 4);
buf.Set(0, center + Vector2D::FromAngle(arc_start) * scale_inner);
buf.Set(1, center + Vector2D::FromAngle(arc_start) * scale_outer);
buf.Set(2, center + Vector2D::FromAngle(arc_end) * scale_inner);
buf.Set(3, center + Vector2D::FromAngle(arc_end) * scale_outer);
buf.Draw(GL_LINES);
} }
void OpenGLWrapper::SetLineColour(wxColour col, float alpha, int width) {
line_r = col.Red() / 255.f;
/// @brief Set line colour line_g = col.Green() / 255.f;
/// @param col line_b = col.Blue() / 255.f;
/// @param alpha line_a = alpha;
/// @param width line_width = width;
///
void OpenGLWrapper::SetLineColour(wxColour col,float alpha,int width) {
r1 = col.Red()/255.0f;
g1 = col.Green()/255.0f;
b1 = col.Blue()/255.0f;
a1 = alpha;
lw = width;
} }
void OpenGLWrapper::SetFillColour(wxColour col, float alpha) {
fill_r = col.Red() / 255.f;
/// @brief Set fill colour fill_g = col.Green() / 255.f;
/// @param col fill_b = col.Blue() / 255.f;
/// @param alpha fill_a = alpha;
///
void OpenGLWrapper::SetFillColour(wxColour col,float alpha) {
r2 = col.Red()/255.0f;
g2 = col.Green()/255.0f;
b2 = col.Blue()/255.0f;
a2 = alpha;
} }
/// @brief Line
///
void OpenGLWrapper::SetModeLine() const { void OpenGLWrapper::SetModeLine() const {
glColor4f(r1,g1,b1,a1); glColor4f(line_r, line_g, line_b, line_a);
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glLineWidth(lw); glLineWidth(line_width);
glEnable(GL_LINE_SMOOTH); if (smooth)
glEnable(GL_LINE_SMOOTH);
else
glDisable(GL_LINE_SMOOTH);
} }
/// @brief Fill
///
void OpenGLWrapper::SetModeFill() const { void OpenGLWrapper::SetModeFill() const {
glColor4f(r2,g2,b2,a2); glColor4f(fill_r, fill_g, fill_b, fill_a);
if (a2 == 1.0f) glDisable(GL_BLEND); if (fill_a == 1.f) glDisable(GL_BLEND);
else { else {
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} }
} }
void OpenGLWrapper::SetInvert() {
glEnable(GL_COLOR_LOGIC_OP);
glLogicOp(GL_INVERT);
// GL_LINE_SMOOTH combines badly with inverting
/// @brief Is extension supported? smooth = false;
/// @param ext
/// @return
///
bool OpenGLWrapper::IsExtensionSupported(const char *ext) {
char *extList = (char*) glGetString(GL_EXTENSIONS);
if (!extList) return false;
return strstr(extList, ext) != NULL;
} }
void OpenGLWrapper::ClearInvert() {
glDisable(GL_COLOR_LOGIC_OP);
smooth = true;
}
bool OpenGLWrapper::IsExtensionSupported(const char *ext) {
char *extList = (char * )glGetString(GL_EXTENSIONS);
return extList && !!strstr(extList, ext);
}
void OpenGLWrapper::DrawLines(size_t dim, std::vector<float> const& lines) {
DrawLines(dim, &lines[0], lines.size() / dim);
}
/// DOCME void OpenGLWrapper::DrawLines(size_t dim, std::vector<float> const& lines, size_t c_dim, std::vector<float> const& colors) {
wxMutex OpenGLWrapper::glMutex; 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<float> 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<float> const& points, std::vector<int> &start, std::vector<int> &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;
}
}

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -34,41 +21,13 @@
/// @ingroup video_output /// @ingroup video_output
/// ///
#include "vector2d.h"
#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
/// DOCME
typedef GLuint GLhandleARB;
#endif
#ifndef AGI_PRE #ifndef AGI_PRE
#include <wx/thread.h> #include <vector>
#include <wx/colour.h>
#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<type>(glGetProc(#name)); \
if (!name) { \
name = reinterpret_cast<type>(& name ## Fallback); \
}
#endif #endif
class wxColour;
/// DOCME /// DOCME
/// @class OpenGLWrapper /// @class OpenGLWrapper
@ -76,55 +35,51 @@ typedef GLuint GLhandleARB;
/// ///
/// DOCME /// DOCME
class OpenGLWrapper { 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 bool transform_pushed;
void PrepareTransform();
/// DOCME
/// DOCME
float r1,g1,b1,a1;
/// DOCME
/// DOCME
/// DOCME
/// DOCME
float r2,g2,b2,a2;
/// DOCME
int lw;
public: public:
OpenGLWrapper(); OpenGLWrapper();
void SetLineColour(wxColour col, float alpha = 1.0f, int width = 1);
/// DOCME void SetFillColour(wxColour col, float alpha = 1.0f);
static wxMutex glMutex;
void SetLineColour(wxColour col,float alpha=1.0f,int width=1);
void SetFillColour(wxColour col,float alpha=1.0f);
void SetModeLine() const; void SetModeLine() const;
void SetModeFill() 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 void SetInvert();
/// @param x void ClearInvert();
/// @param y
/// @param radius void SetScale(Vector2D scale);
/// void SetOrigin(Vector2D origin);
void DrawCircle(float x,float y,float radius) const { DrawEllipse(x,y,radius,radius); } void SetRotation(float x, float y, float z);
void DrawRectangle(float x1,float y1,float x2,float y2) const; void ResetTransform();
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 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<float> const& lines);
void DrawLines(size_t dim, std::vector<float> const& lines, size_t c_dim, std::vector<float> const& colors);
void DrawLines(size_t dim, const float *lines, size_t n);
void DrawLineStrip(size_t dim, std::vector<float> 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<float> const& points, std::vector<int> &start, std::vector<int> &count, Vector2D video_size, bool invert);
static bool IsExtensionSupported(const char *ext); static bool IsExtensionSupported(const char *ext);
}; };

View file

@ -34,5 +34,5 @@ namespace toolbar {
/// @param context Project context /// @param context Project context
/// @param hotkey Hotkey context for the tooltip /// @param hotkey Hotkey context for the tooltip
void AttachToolbar(wxFrame *frame, std::string const& name, agi::Context *context, std::string const& hotkey); 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);
} }

View file

@ -474,43 +474,43 @@
"key" : "Left" "key" : "Left"
} }
], ],
"visual typesetting set tool crosshair" : [ "video/tool/cross" : [
{ {
"modifiers" : [], "modifiers" : [],
"key" : "A" "key" : "A"
} }
], ],
"visual typesetting set tool drag" : [ "video/tool/drag" : [
{ {
"modifiers" : [], "modifiers" : [],
"key" : "S" "key" : "S"
} }
], ],
"visual typesetting set tool rectangle clip" : [ "video/tool/clip" : [
{ {
"modifiers" : [], "modifiers" : [],
"key" : "H" "key" : "H"
} }
], ],
"visual typesetting set tool rotate xy" : [ "video/tool/rotate/xy" : [
{ {
"modifiers" : [], "modifiers" : [],
"key" : "F" "key" : "F"
} }
], ],
"visual typesetting set tool rotate z" : [ "video/tool/rotate/z" : [
{ {
"modifiers" : [], "modifiers" : [],
"key" : "D" "key" : "D"
} }
], ],
"visual typesetting set tool scale" : [ "video/tool/scale" : [
{ {
"modifiers" : [], "modifiers" : [],
"key" : "G" "key" : "G"
} }
], ],
"visual typesetting set tool vector clip" : [ "video/tool/vector_clip" : [
{ {
"modifiers" : [], "modifiers" : [],
"key" : "J" "key" : "J"

View file

@ -61,5 +61,16 @@
"", "",
"app/options", "app/options",
"grid/tag/cycle_hiding" "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"
] ]
} }

View file

@ -37,192 +37,164 @@
#include "config.h" #include "config.h"
#ifndef AGI_PRE #ifndef AGI_PRE
#include <limits>
#include <wx/tokenzr.h> #include <wx/tokenzr.h>
#endif #endif
#include <limits>
#include "spline.h" #include "spline.h"
#include "utils.h"
#include "video_display.h"
/// @brief Spline constructor #include "utils.h"
Spline::Spline(const VideoDisplay &scale) : scale(scale) { #include "visual_tool.h"
Spline::Spline(const VisualToolBase &scale) : scale(scale) {
} }
/// @brief Encode to ASS /// @brief Encode to ASS
wxString Spline::EncodeToASS() { wxString Spline::EncodeToASS() {
wxString result; wxString result;
char lastCommand = 0; result.reserve(size() * 10);
char last = 0;
// Insert each element for (iterator cur = begin(); cur != end(); ++cur) {
for (iterator cur=begin();cur!=end();cur++) {
// Each curve
switch (cur->type) { switch (cur->type) {
case CURVE_POINT: { case SplineCurve::POINT:
if (lastCommand != 'm') { if (last != 'm') {
result += "m "; result += "m ";
lastCommand = 'm'; last = 'm';
} }
int x = cur->p1.x; result += scale.ToScriptCoords(cur->p1).DStr(' ');
int y = cur->p1.y;
scale.ToScriptCoords(&x, &y);
result += wxString::Format("%i %i ", x, y);
break; break;
}
case CURVE_LINE: { case SplineCurve::LINE:
if (lastCommand != 'l') { if (last != 'l') {
result += "l "; result += "l ";
lastCommand = 'l'; last = 'l';
} }
int x = cur->p2.x; result += scale.ToScriptCoords(cur->p2).DStr(' ');
int y = cur->p2.y;
scale.ToScriptCoords(&x, &y);
result += wxString::Format("%i %i ", x, y);
break; break;
}
case CURVE_BICUBIC: { case SplineCurve::BICUBIC:
if (lastCommand != 'b') { if (last != 'b') {
result += "b "; result += "b ";
lastCommand = 'b'; last = 'b';
} }
int x2 = cur->p2.x; result += scale.ToScriptCoords(cur->p2).DStr(' ');
int y2 = cur->p2.y; result += scale.ToScriptCoords(cur->p3).DStr(' ');
int x3 = cur->p3.x; result += scale.ToScriptCoords(cur->p4).DStr(' ');
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);
break; break;
}
default: break; default: break;
} }
result += " ";
} }
return result; return result;
} }
/// @brief Decode from ASS
/// @param str
void Spline::DecodeFromASS(wxString str) { void Spline::DecodeFromASS(wxString str) {
// Clear current // Clear current
clear(); clear();
std::vector<int> stack; std::vector<float> stack;
// Prepare // Prepare
char lastCommand = 'm'; char command = 'm';
int x = 0; Vector2D pt;
int y = 0;
// Tokenize the string // Tokenize the string
wxStringTokenizer tkn(str," "); wxStringTokenizer tkn(str, " ");
while (tkn.HasMoreTokens()) { while (tkn.HasMoreTokens()) {
wxString token = tkn.GetNextToken(); wxString token = tkn.GetNextToken();
double n;
// Got a number if (token.ToCDouble(&n)) {
if (token.IsNumber()) {
long n;
token.ToLong(&n);
stack.push_back(n); stack.push_back(n);
// Move // Move
if (stack.size() == 2 && lastCommand == 'm') { if (stack.size() == 2 && command == 'm') {
scale.FromScriptCoords(&stack[0], &stack[1]); pt = scale.FromScriptCoords(Vector2D(stack[0], stack[1]));
SplineCurve curve;
x = curve.p1.x = stack[0];
y = curve.p1.y = stack[1];
curve.type = CURVE_POINT;
stack.clear(); stack.clear();
push_back(curve);
push_back(pt);
} }
// Line // Line
if (stack.size() == 2 && lastCommand == 'l') { if (stack.size() == 2 && command == 'l') {
scale.FromScriptCoords(&stack[0], &stack[1]); SplineCurve curve(pt, scale.FromScriptCoords(Vector2D(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();
push_back(curve); push_back(curve);
pt = curve.p2;
stack.clear();
} }
// Bicubic // Bicubic
else if (stack.size() == 6 && lastCommand == 'b') { else if (stack.size() == 6 && command == 'b') {
scale.FromScriptCoords(&stack[0], &stack[1]); SplineCurve curve(pt,
scale.FromScriptCoords(&stack[2], &stack[3]); scale.FromScriptCoords(Vector2D(stack[0], stack[1])),
scale.FromScriptCoords(&stack[4], &stack[5]); scale.FromScriptCoords(Vector2D(stack[2], stack[3])),
SplineCurve curve; scale.FromScriptCoords(Vector2D(stack[4], stack[5])));
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();
push_back(curve); push_back(curve);
}
// Close pt = curve.p4;
else if (lastCommand == 'c') {
stack.clear(); stack.clear();
} }
} }
// Got something else // Got something else
else { else if (token.size() == 1) {
if (token == "m") lastCommand = 'm'; command = token[0];
else if (token == "l") lastCommand = 'l'; stack.clear();
else if (token == "b") lastCommand = 'b';
else if (token == "n") lastCommand = 'n';
else if (token == "s") lastCommand = 's';
else if (token == "c") lastCommand = 'c';
} }
} }
} }
/// @brief Moves a specific point in the spline void Spline::MovePoint(iterator curve,int point,Vector2D pos) {
/// @param curveIndex
/// @param point
/// @param pos
void Spline::MovePoint(iterator curve,int point,Vector2D const& pos) {
iterator prev = curve; iterator prev = curve;
if (curve != begin()) --prev; if (curve != begin()) --prev;
iterator next = curve; iterator next = curve;
++next; ++next;
if (next != end() && next->type == CURVE_POINT) next = end(); if (next != end() && next->type == SplineCurve::POINT)
next = end();
// Modify // Modify
if (point == 0) { if (point == 0) {
curve->p1 = pos; curve->p1 = pos;
if (curve != begin() && curve->type != CURVE_POINT) prev->EndPoint() = pos; if (curve != begin() && curve->type != SplineCurve::POINT)
if (next != end() && curve->type == CURVE_POINT) next->p1 = pos; prev->EndPoint() = pos;
if (next != end() && curve->type == SplineCurve::POINT)
next->p1 = pos;
} }
else if (point == 1) { else if (point == 1) {
curve->p2 = pos; 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) { else if (point == 2) {
curve->p3 = pos; curve->p3 = pos;
} }
else if (point == 3) { else if (point == 3) {
curve->p4 = pos; curve->p4 = pos;
if (next != end()) next->p1 = pos; if (next != end())
next->p1 = pos;
} }
} }
/// @brief Gets a list of points in the curve static int render_bicubic(Spline::iterator cur, std::vector<float> &points) {
/// @param points int len = int(
/// @param pointCurve (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<float>& points, std::vector<int>& first, std::vector<int>& count) { void Spline::GetPointList(std::vector<float>& points, std::vector<int>& first, std::vector<int>& count) {
points.clear(); points.clear();
first.clear(); first.clear();
@ -232,106 +204,65 @@ void Spline::GetPointList(std::vector<float>& points, std::vector<int>& first, s
int curCount = 0; int curCount = 0;
// Generate points for each curve // Generate points for each curve
for (iterator cur = begin();cur!=end();cur++) { for (iterator cur = begin(); cur != end(); ++cur) {
switch (cur->type) { switch (cur->type) {
case CURVE_POINT: case SplineCurve::POINT:
if (curCount > 0) { if (curCount > 0)
count.push_back(curCount); count.push_back(curCount);
}
// start new path // start new path
first.push_back(points.size() / 2); first.push_back(points.size() / 2);
points.push_back(cur->p1.x); points.push_back(cur->p1.X());
points.push_back(cur->p1.y); points.push_back(cur->p1.Y());
curCount = 1; curCount = 1;
break; 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 case SplineCurve::LINE:
int len = (int)((p2-p1).Len() + (p3-p2).Len() + (p4-p3).Len()); points.push_back(cur->p2.X());
int steps = len/8; points.push_back(cur->p2.Y());
++curCount;
// 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;
break; break;
}
case SplineCurve::BICUBIC:
curCount += render_bicubic(cur, points);
break;
default: break; default: break;
} }
} }
count.push_back(curCount); count.push_back(curCount);
} }
void Spline::GetPointList(std::vector<float> &points, iterator curve) { void Spline::GetPointList(std::vector<float> &points, iterator curve) {
points.clear(); points.clear();
if (curve == end()) return; if (curve == end()) return;
switch (curve->type) { switch (curve->type) {
case CURVE_LINE: case SplineCurve::LINE:
points.push_back(curve->p1.x); points.push_back(curve->p1.X());
points.push_back(curve->p1.y); points.push_back(curve->p1.Y());
points.push_back(curve->p2.x); points.push_back(curve->p2.X());
points.push_back(curve->p2.y); points.push_back(curve->p2.Y());
break; 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 case SplineCurve::BICUBIC:
int len = (int)((p2-p1).Len() + (p3-p2).Len() + (p4-p3).Len()); render_bicubic(curve, points);
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);
}
break; break;
}
default: break; default: break;
} }
} }
/// @brief t value and curve of the point closest to reference void Spline::GetClosestParametricPoint(Vector2D reference,iterator &curve,float &t,Vector2D &pt) {
/// @param reference
/// @param curve
/// @param t
/// @param pt
void Spline::GetClosestParametricPoint(Vector2D const& reference,iterator &curve,float &t,Vector2D &pt) {
curve = end(); curve = end();
t = 0.f; t = 0.f;
if (empty()) return; if (empty()) return;
// Close the shape // Close the shape
SplineCurve pad; push_back(SplineCurve(back().EndPoint(), front().p1));
pad.p1 = back().EndPoint();
pad.p2 = front().p1;
pad.type = CURVE_LINE;
push_back(pad);
float closest = std::numeric_limits<float>::infinity(); float closest = std::numeric_limits<float>::infinity();
for (iterator cur = begin();cur!=end();cur++) { for (iterator cur = begin(); cur != end(); ++cur) {
float param = cur->GetClosestParam(reference); float param = cur->GetClosestParam(reference);
Vector2D p1 = cur->GetPoint(param); Vector2D p1 = cur->GetPoint(param);
float dist = (p1-reference).SquareLen(); float dist = (p1-reference).SquareLen();
@ -351,19 +282,14 @@ void Spline::GetClosestParametricPoint(Vector2D const& reference,iterator &curve
pop_back(); pop_back();
} }
/// @brief Point closest to reference Vector2D Spline::GetClosestPoint(Vector2D reference) {
/// @param reference
/// @return
Vector2D Spline::GetClosestPoint(Vector2D const& reference) {
iterator curve; iterator curve;
float t; float t;
Vector2D point; Vector2D point;
GetClosestParametricPoint(reference,curve,t,point); GetClosestParametricPoint(reference, curve, t, point);
return point; return point;
} }
/// @brief Smoothes the spline
/// @param smooth
void Spline::Smooth(float smooth) { void Spline::Smooth(float smooth) {
// See if there are enough curves // See if there are enough curves
if (size() < 3) return; if (size() < 3) return;
@ -371,13 +297,12 @@ void Spline::Smooth(float smooth) {
// Smooth curve // Smooth curve
iterator curve1 = end(); iterator curve1 = end();
--curve1; --curve1;
for (iterator cur = begin(); cur != end();) { for (iterator cur = begin(); cur != end(); ) {
iterator curve0 = curve1; iterator curve0 = curve1;
curve1 = cur; curve1 = cur;
cur++; ++cur;
iterator curve2 = cur == end() ? begin() : cur; iterator curve2 = cur == end() ? begin() : cur;
// Smooth curve curve1->Smooth(curve0->p1, curve2->p2, smooth);
curve1->Smooth(curve0->p1,curve2->p2,smooth);
} }
} }

View file

@ -43,29 +43,41 @@
#include "spline_curve.h" #include "spline_curve.h"
class VideoDisplay; class VisualToolBase;
/// DOCME /// DOCME
/// @class Spline /// @class Spline
/// @brief DOCME /// @brief DOCME
class Spline : private std::list<SplineCurve> { class Spline : private std::list<SplineCurve> {
private: const VisualToolBase &scale;
const VideoDisplay &scale;
public: public:
Spline(const VideoDisplay &scale); Spline(const VisualToolBase &scale);
/// Encode to an ASS vector drawing
wxString EncodeToASS(); wxString EncodeToASS();
/// Decode an ASS vector drawing
void DecodeFromASS(wxString str); 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); void Smooth(float smooth=1.0f);
/// Gets a list of points in the curve
void GetPointList(std::vector<float>& points, std::vector<int>& first, std::vector<int>& count); void GetPointList(std::vector<float>& points, std::vector<int>& first, std::vector<int>& count);
/// Gets a list of points in the curve
void GetPointList(std::vector<float> &points, iterator curve); void GetPointList(std::vector<float> &points, iterator curve);
void GetClosestParametricPoint(Vector2D const& reference, iterator& curve, float &t, Vector2D &point); /// Get t value and curve of the point closest to reference
Vector2D GetClosestPoint(Vector2D const& reference); void GetClosestParametricPoint(Vector2D reference, iterator& curve, float &t, Vector2D &point);
Vector2D GetClosestControlPoint(Vector2D const& reference); /// 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 // This list intentionally excludes things specific to std::list
using std::list<SplineCurve>::value_type; using std::list<SplineCurve>::value_type;

View file

@ -34,145 +34,103 @@
/// @ingroup visual_ts /// @ingroup visual_ts
/// ///
///////////
// Headers
#include "config.h" #include "config.h"
#include "spline_curve.h" #include "spline_curve.h"
#include "utils.h" #include "utils.h"
/// @brief Curve constructor #ifndef AGI_PRE
/// #include <limits>
SplineCurve::SplineCurve() { #include <numeric>
type = CURVE_INVALID; #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 void SplineCurve::Split(SplineCurve &c1, SplineCurve &c2, float t) {
/// @param c1 if (type == LINE) {
/// @param c2 c1 = SplineCurve(p1, p1 * (1 - t) + p2 * t);
/// @param t c2 = SplineCurve(c1.p2, p2);
///
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;
} }
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 c1 = SplineCurve(p1, p12, p123, p1234);
else if (type == CURVE_BICUBIC) { c2 = SplineCurve(p1234, p234, p34, p4);
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;
} }
} }
/// @brief Based on http://antigrain.com/research/bezier_interpolation/index.html Smoothes the curve void SplineCurve::Smooth(Vector2D p0, Vector2D p3, float smooth) {
/// @param P0 if (type != LINE || p1 == p2) return;
/// @param P3 smooth = mid(0.f, smooth, 1.f);
/// @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;
// Calculate intermediate points // Calculate intermediate points
Vector2D c1 = (P0+P1)/2.f; Vector2D c1 = (p0 + p1) / 2.f;
Vector2D c2 = (P1+P2)/2.f; Vector2D c2 = (p1 + p2) / 2.f;
Vector2D c3 = (P2+P3)/2.f; Vector2D c3 = (p2 + p3) / 2.f;
float len1 = (P1-P0).Len();
float len2 = (P2-P1).Len(); float len1 = (p1 - p0).Len();
float len3 = (P3-P2).Len(); float len2 = (p2 - p1).Len();
float k1 = len1/(len1+len2); float len3 = (p3 - p2).Len();
float k2 = len2/(len2+len3);
Vector2D m1 = c1+(c2-c1)*k1; float k1 = len1 / (len1 + len2);
Vector2D m2 = c2+(c3-c2)*k2; float k2 = len2 / (len2 + len3);
Vector2D m1 = c1 + (c2 - c1) * k1;
Vector2D m2 = c2 + (c3 - c2) * k2;
// Set curve points // Set curve points
p4 = p2; p4 = p2;
p2 = m1+(c2-m1)*smooth + P1 - m1; p3 = m2 + (c2 - m2) * smooth + p2 - m2;
p3 = m2+(c2-m2)*smooth + P2 - m2; p2 = m1 + (c2 - m1) * smooth + p1 - m1;
type = CURVE_BICUBIC; type = BICUBIC;
} }
/// @brief Get a point
/// @param t
/// @return
///
Vector2D SplineCurve::GetPoint(float t) const { Vector2D SplineCurve::GetPoint(float t) const {
if (type == CURVE_POINT) return p1; float u = 1.f - t;
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;
}
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() { Vector2D& SplineCurve::EndPoint() {
switch (type) { switch (type) {
case CURVE_POINT: return p1; case POINT: return p1;
case CURVE_LINE: return p2; case LINE: return p2;
case CURVE_BICUBIC: return p4; case BICUBIC: return p4;
default: return p1; default: return p1;
} }
} }
/// @brief Get point closest to reference Vector2D SplineCurve::GetClosestPoint(Vector2D ref) const {
/// @param ref
/// @return
///
Vector2D SplineCurve::GetClosestPoint(Vector2D const& ref) const {
return GetPoint(GetClosestParam(ref)); return GetPoint(GetClosestParam(ref));
} }
/// @brief Get value of parameter closest to point float SplineCurve::GetClosestParam(Vector2D ref) const {
/// @param ref if (type == LINE)
/// @return return GetClosestSegmentPart(p1, p2, ref);
///
float SplineCurve::GetClosestParam(Vector2D const& ref) const { if (type == BICUBIC) {
if (type == CURVE_LINE) {
return GetClosestSegmentPart(p1,p2,ref);
}
if (type == CURVE_BICUBIC) {
int steps = 100; int steps = 100;
float bestDist = 80000000.f; float bestDist = std::numeric_limits<float>::max();
float bestT = 0.f; float bestT = 0.f;
for (int i=0;i<=steps;i++) { for (int i = 0; i <= steps; ++i) {
float t = float(i)/float(steps); float t = i / float(steps);
float dist = (GetPoint(t)-ref).Len(); float dist = (GetPoint(t) - ref).SquareLen();
if (dist < bestDist) { if (dist < bestDist) {
bestDist = dist; bestDist = dist;
bestT = t; bestT = t;
@ -180,45 +138,30 @@ float SplineCurve::GetClosestParam(Vector2D const& ref) const {
} }
return bestT; return bestT;
} }
return 0.f; return 0.f;
} }
/// @brief Quick distance float SplineCurve::GetQuickDistance(Vector2D ref) const {
/// @param ref if (type == BICUBIC) {
/// @return float lens[] = {
/// GetClosestSegmentDistance(p1, p2, ref),
float SplineCurve::GetQuickDistance(Vector2D const& ref) const { GetClosestSegmentDistance(p2, p3, ref),
using std::min; GetClosestSegmentDistance(p3, p4, ref),
if (type == CURVE_BICUBIC) { GetClosestSegmentDistance(p4, p1, ref),
float len1 = GetClosestSegmentDistance(p1,p2,ref); GetClosestSegmentDistance(p1, p3, ref),
float len2 = GetClosestSegmentDistance(p2,p3,ref); GetClosestSegmentDistance(p2, p4, ref)
float len3 = GetClosestSegmentDistance(p3,p4,ref); };
float len4 = GetClosestSegmentDistance(p4,p1,ref); return *std::min_element(lens, lens + 6);
float len5 = GetClosestSegmentDistance(p1,p3,ref);
float len6 = GetClosestSegmentDistance(p2,p4,ref);
return min(min(min(len1,len2),min(len3,len4)),min(len5,len6));
} }
return (GetClosestPoint(ref) - ref).Len();
// Something else
else return (GetClosestPoint(ref)-ref).Len();
} }
/// @brief Closest t in segment p1-p2 to point p3 float SplineCurve::GetClosestSegmentPart(Vector2D pt1, Vector2D pt2, Vector2D pt3) const {
/// @param pt1 return mid(0.f, (pt3 - pt1).Dot(pt2 - pt1) / (pt2 - pt1).SquareLen(), 1.f);
/// @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);
} }
/// @brief Closest distance between p3 and segment p1-p2 float SplineCurve::GetClosestSegmentDistance(Vector2D pt1, Vector2D pt2, Vector2D pt3) const {
/// @param pt1 float t = GetClosestSegmentPart(pt1, pt2, pt3);
/// @param pt2 return (pt1 * (1.f - t) + pt2 * t - pt3).Len();
/// @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();
} }

View file

@ -34,57 +34,54 @@
/// @ingroup visual_ts /// @ingroup visual_ts
/// ///
///////////
// Headers
#include "vector2d.h" #include "vector2d.h"
/// DOCME
enum CurveType {
/// DOCME
CURVE_INVALID,
/// DOCME
CURVE_POINT,
/// DOCME
CURVE_LINE,
/// DOCME
CURVE_BICUBIC
};
/// DOCME /// DOCME
/// @class SplineCurve /// @class SplineCurve
/// @brief DOCME /// @brief DOCME
/// ///
/// DOCME /// DOCME
class SplineCurve { class SplineCurve {
private: /// Closest t in segment p1-p2 to point p3
float GetClosestSegmentPart(Vector2D const& p1,Vector2D const& p2,Vector2D const& p3) const; float GetClosestSegmentPart(Vector2D p1, Vector2D p2, Vector2D p3) const;
float GetClosestSegmentDistance(Vector2D const& p1,Vector2D const& p2,Vector2D const& p3) const;
/// Closest distance between p3 and segment p1-p2
float GetClosestSegmentDistance(Vector2D p1, Vector2D p2, Vector2D p3) const;
public: public:
enum CurveType {
POINT,
LINE,
BICUBIC
};
/// DOCME Vector2D p1;
Vector2D p2;
/// DOCME Vector2D p3;
Vector2D p4;
/// DOCME
/// DOCME
Vector2D p1,p2,p3,p4;
/// DOCME /// DOCME
CurveType type; CurveType type;
SplineCurve(); SplineCurve(Vector2D p1 = Vector2D());
void Split(SplineCurve &c1,SplineCurve &c2,float t=0.5); SplineCurve(Vector2D p1, Vector2D p2);
void Smooth(Vector2D const& prev,Vector2D const& next,float smooth=1.0f); 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 GetPoint(float t) const;
Vector2D& EndPoint(); Vector2D& EndPoint();
Vector2D GetClosestPoint(Vector2D const& ref) const; /// Get point on the curve closest to reference
float GetClosestParam(Vector2D const& ref) const; Vector2D GetClosestPoint(Vector2D ref) const;
float GetQuickDistance(Vector2D const& 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;
}; };

View file

@ -74,7 +74,6 @@
#include "utils.h" #include "utils.h"
#include "validators.h" #include "validators.h"
#include "video_context.h" #include "video_context.h"
#include "video_display.h"
enum { enum {
BUTTON_BOLD = 1300, BUTTON_BOLD = 1300,

View file

@ -166,8 +166,8 @@ namespace {
} }
public: public:
Toolbar(wxWindow *parent, std::string const& name, agi::Context *c, std::string const& ht_context) Toolbar(wxWindow *parent, std::string const& name, agi::Context *c, std::string const& ht_context, bool vertical)
: wxToolBar(parent, -1, wxDefaultPosition, wxDefaultSize, wxTB_FLAT | wxTB_HORIZONTAL) : wxToolBar(parent, -1, wxDefaultPosition, wxDefaultSize, wxTB_FLAT | (vertical ? wxTB_VERTICAL : wxTB_HORIZONTAL))
, name(name) , name(name)
, context(c) , context(c)
, ht_context(ht_context) , ht_context(ht_context)
@ -182,10 +182,10 @@ namespace {
namespace toolbar { namespace toolbar {
void AttachToolbar(wxFrame *frame, std::string const& name, agi::Context *c, std::string const& hotkey) { 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) { wxToolBar *GetToolbar(wxWindow *parent, std::string const& name, agi::Context *c, std::string const& hotkey, bool vertical) {
return new Toolbar(parent, name, c, hotkey); return new Toolbar(parent, name, c, hotkey, vertical);
} }
} }

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -34,238 +21,77 @@
/// @ingroup utility visual_ts /// @ingroup utility visual_ts
/// ///
///////////
// Headers
#include "config.h" #include "config.h"
#ifndef AGI_PRE
#include <math.h>
#endif
#include "vector2d.h" #include "vector2d.h"
#ifndef AGI_PRE
#include <limits>
/// @brief Null constructor #include <wx/numformatter.h>
/// #endif
Vector2D::Vector2D () {
x = y = 0; Vector2D operator *(float f, Vector2D v) {
return Vector2D(v.X() * f, v.Y() * f);
} }
Vector2D operator /(float f, Vector2D v) {
return Vector2D(f / v.X(), f / v.Y());
/// @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(v.X() + f, v.Y() + f);
/// @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(f - v.X(), f - v.Y());
/// @brief Assignment
/// @param param
///
void Vector2D::operator = (const Vector2D param) {
x = param.x;
y = param.y;
} }
Vector2D Vector2D::Unit() const {
float len = Len();
/// @brief Comparison if (len == 0)
/// @param param return Vector2D(0, 0);
/// @return return *this / len;
///
bool Vector2D::operator == (const Vector2D param) const {
return ((x == param.x) && (y == param.y));
} }
Vector2D Vector2D::SingleAxis() const {
/// @brief DOCME if (abs(x) < abs(y))
/// @param param return Vector2D(0, y);
/// @return else
/// return Vector2D(x, 0);
bool Vector2D::operator != (const Vector2D param) const {
return ((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 Adition
/// @param param
/// @return
///
Vector2D Vector2D::operator + (const Vector2D param) const {
return Vector2D(x + param.x,y + param.y);
} }
Vector2D Vector2D::Min(Vector2D param) const {
/// @brief DOCME return Vector2D(std::min(x, param.x), std::min(y, param.y));
/// @param param
/// @return
///
Vector2D Vector2D::operator += (const Vector2D param) {
x += param.x;
y += param.y;
return *this;
} }
Vector2D Vector2D::Round(float step) const {
return Vector2D(floorf(x / step + .5f) * step, floorf(y / step + .5f) * step);
/// @brief Subtraction
/// @param param
/// @return
///
Vector2D Vector2D::operator - (const Vector2D param) const {
return Vector2D(x - param.x,y - param.y);
} }
Vector2D::operator unspecified_bool_type() const {
/// @brief DOCME return *this == Bad() ? 0 : &Vector2D::x;
/// @param param
/// @return
///
Vector2D Vector2D::operator -= (const Vector2D param) {
x -= param.x;
y -= param.y;
return *this;
} }
Vector2D Vector2D::Bad() {
return Vector2D(std::numeric_limits<float>::min(), std::numeric_limits<float>::min());
/// @brief Negate
/// @return
///
Vector2D Vector2D::operator - () const {
return Vector2D(-x,-y);
} }
wxString Vector2D::PStr(char sep) const {
return "(" + Str(sep) + ")";
/// @brief Multiplication by scalar
/// @param param
/// @return
///
Vector2D Vector2D::operator * (float param) const {
return Vector2D(x * param,y * param);
} }
wxString Vector2D::DStr(char sep) const {
/// @brief DOCME return wxString::Format("%d%c%d", (int)x, sep, (int)y);
/// @param param
/// @return
///
Vector2D Vector2D::operator *= (float param) {
x *= param;
y *= param;
return *this;
} }
wxString Vector2D::Str(char sep) const {
/// @brief DOCME return
/// @param f wxNumberFormatter::ToString(x, 3, wxNumberFormatter::Style_NoTrailingZeroes) +
/// @param v sep +
/// @return wxNumberFormatter::ToString(y, 3, wxNumberFormatter::Style_NoTrailingZeroes);
///
Vector2D operator * (float f,const Vector2D &v) {
return Vector2D(v.x * f,v.y * f);
} }
/// @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);
}

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -34,8 +21,13 @@
/// @ingroup utility visual_ts /// @ingroup utility visual_ts
/// ///
#pragma once
#ifndef AGI_PRE
#include <cmath>
#include <wx/gdicmn.h>
#endif
/// DOCME /// DOCME
/// @class Vector2D /// @class Vector2D
@ -43,53 +35,62 @@
/// ///
/// DOCME /// DOCME
class Vector2D { class Vector2D {
float x, y;
typedef float Vector2D::*unspecified_bool_type;
public: 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 bool operator ==(const Vector2D r) const { return x == r.x && y == r.y; }
float x,y; bool operator !=(const Vector2D r) const { return x != r.x || y != r.y; }
operator unspecified_bool_type() const;
Vector2D (); Vector2D operator -() const { return Vector2D(-x, -y); }
Vector2D (float _x,float _y); Vector2D operator +(const Vector2D r) const { return Vector2D(x + r.x, y + r.y); }
Vector2D (const Vector2D &vec); 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); Vector2D Unit() const;
bool operator == (const Vector2D param) const; Vector2D SingleAxis() const;
bool operator != (const Vector2D param) const;
Vector2D operator - () const; Vector2D Perpendicular() const { return Vector2D(-y, x); }
Vector2D operator + (const Vector2D param) const;
Vector2D operator - (const Vector2D param) const;
Vector2D operator * (float param) const;
Vector2D operator / (float param) const;
Vector2D operator += (const Vector2D param); Vector2D Max(Vector2D param) const;
Vector2D operator -= (const Vector2D param); Vector2D Min(Vector2D param) const;
Vector2D operator *= (float param); Vector2D Round(float step) const;
Vector2D operator /= (float param);
Vector2D Unit () const; float Cross(const Vector2D param) const { return x * param.y - y * param.x; }
float Cross (const Vector2D param) const; float Dot(const Vector2D param) const { return x * param.x + y * param.y; }
float Dot (const Vector2D param) const;
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 /// Get as string with given separator
/// @return wxString Str(char sep = ',') const;
/// /// Get as string surrounded by parentheses with given separator
float Length () const { return Len(); } wxString PStr(char sep = ',') const;
float SquareLen () const; /// Get as string with given separator with values rounded to ints
wxString DStr(char sep = ',') const;
/// @brief DOCME static Vector2D FromAngle(float angle) { return Vector2D(cos(-angle), sin(-angle)); }
/// static Vector2D Bad();
float SquareLength () const { return SquareLen(); }
}; };
Vector2D operator * (float f, Vector2D v);
//////////////////// Vector2D operator / (float f, Vector2D v);
// Global operators Vector2D operator + (float f, Vector2D v);
Vector2D operator * (float f,const Vector2D &v); Vector2D operator - (float f, Vector2D v);
Vector2D operator / (float f,const Vector2D &v);

View file

@ -44,6 +44,7 @@
#endif #endif
#include "include/aegisub/context.h" #include "include/aegisub/context.h"
#include "include/aegisub/toolbar.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.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); zoomBox = new wxComboBox(this, -1, "75%", wxDefaultPosition, wxDefaultSize, choices, wxCB_DROPDOWN);
// Typesetting buttons // Typesetting buttons
visualToolBar = new wxToolBar(this,-1,wxDefaultPosition,wxDefaultSize,wxTB_VERTICAL|wxTB_FLAT|wxTB_NODIVIDER); visualToolBar = toolbar::GetToolbar(this, "visual_tools", context, "Video", true);
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();
// Avoid ugly themed background on Vista and possibly also Win7 // Avoid ugly themed background on Vista and possibly also Win7
visualToolBar->SetBackgroundStyle(wxBG_STYLE_COLOUR); visualToolBar->SetBackgroundStyle(wxBG_STYLE_COLOUR);
visualToolBar->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); visualToolBar->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
// Display
videoDisplay = new VideoDisplay(this,isDetached,zoomBox,this,context); videoDisplay = new VideoDisplay(this,isDetached,zoomBox,this,context);
// Top sizer // Top sizer

View file

@ -92,14 +92,3 @@ public:
VideoBox(wxWindow *parent, bool isDetached, agi::Context *context); VideoBox(wxWindow *parent, bool isDetached, agi::Context *context);
~VideoBox(); ~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,
};

View file

@ -266,7 +266,8 @@ void VideoContext::JumpToFrame(int n) {
frame_n = mid(0, n, GetLength() - 1); frame_n = mid(0, n, GetLength() - 1);
Seek(n); GetFrameAsync(frame_n);
Seek(frame_n);
} }
void VideoContext::JumpToTime(int ms, agi::vfr::Time end) { 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) { 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<AegiVideoFrame> VideoContext::GetFrame(int n, bool raw) { std::tr1::shared_ptr<AegiVideoFrame> 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 { int VideoContext::GetWidth() const {
@ -326,10 +327,6 @@ void VideoContext::SaveSnapshot(bool raw) {
GetFrame(frame_n,raw)->GetImage().SaveFile(path,wxBITMAP_TYPE_PNG); 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() { void VideoContext::NextFrame() {
if (!videoProvider.get() || isPlaying || frame_n == videoProvider->GetFrameCount()) if (!videoProvider.get() || isPlaying || frame_n == videoProvider->GetFrameCount())
return; return;

View file

@ -232,13 +232,6 @@ public:
/// @param end Type of time /// @param end Type of time
void JumpToTime(int ms, agi::vfr::Time end = agi::vfr::START); 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 /// Starting playing the video
void Play(); void Play();
/// Play the next frame then stop /// Play the next frame then stop

View file

@ -56,13 +56,13 @@
#include <GL/glu.h> #include <GL/glu.h>
#endif #endif
#include "include/aegisub/context.h"
#include "include/aegisub/hotkey.h"
#include "include/aegisub/menu.h"
#include "video_display.h" #include "video_display.h"
#include "ass_file.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 "main.h"
#include "threaded_frame_source.h" #include "threaded_frame_source.h"
#include "utils.h" #include "utils.h"
@ -70,13 +70,6 @@
#include "video_box.h" #include "video_box.h"
#include "video_context.h" #include "video_context.h"
#include "visual_tool.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 /// 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 }; 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) : wxGLCanvas (parent, -1, attribList, wxDefaultPosition, wxDefaultSize, 0, wxPanelNameStr)
, alwaysShowTools(OPT_GET("Tool/Visual/Always Show")) , alwaysShowTools(OPT_GET("Tool/Visual/Always Show"))
, con(c) , con(c)
, currentFrame(-1) , w(8)
, w(8), h(8), viewport_x(0), viewport_width(0), viewport_bottom(0), viewport_top(0), viewport_height(0) , 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) , zoomValue(OPT_GET("Video/Default Zoom")->GetInt() * .125 + .125)
, videoOut(new VideoOutGL()) , videoOut(new VideoOutGL())
, activeMode(Video_Mode_Standard)
, toolBar(box->visualSubToolBar) , toolBar(box->visualSubToolBar)
, scriptW(INT_MIN)
, scriptH(INT_MIN)
, zoomBox(zoomBox) , zoomBox(zoomBox)
, box(box) , box(box)
, freeSize(freeSize) , freeSize(freeSize)
@ -120,34 +116,29 @@ VideoDisplay::VideoDisplay(
zoomBox->SetValue(wxString::Format("%g%%", zoomValue * 100.)); zoomBox->SetValue(wxString::Format("%g%%", zoomValue * 100.));
zoomBox->Bind(wxEVT_COMMAND_COMBOBOX_SELECTED, &VideoDisplay::SetZoomFromBox, this); 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); 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->AddVideoOpenListener(&VideoDisplay::OnVideoOpen, this));
slots.push_back(con->videoController->AddARChangeListener(&VideoDisplay::UpdateSize, 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)); Bind(wxEVT_PAINT, std::tr1::bind(&VideoDisplay::Render, this));
if (freeSize) { if (freeSize) {
Bind(wxEVT_SIZE, &VideoDisplay::OnSizeEvent, this); Bind(wxEVT_SIZE, &VideoDisplay::OnSizeEvent, this);
} }
Bind(wxEVT_CONTEXT_MENU, &VideoDisplay::OnContextMenu, this); Bind(wxEVT_CONTEXT_MENU, &VideoDisplay::OnContextMenu, this);
Bind(wxEVT_ENTER_WINDOW, &VideoDisplay::OnMouseEvent, this);
Bind(wxEVT_KEY_DOWN, &VideoDisplay::OnKeyDown, 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_DCLICK, &VideoDisplay::OnMouseEvent, this);
Bind(wxEVT_LEFT_DOWN, &VideoDisplay::OnMouseEvent, this); Bind(wxEVT_LEFT_DOWN, &VideoDisplay::OnMouseEvent, this);
Bind(wxEVT_LEFT_UP, &VideoDisplay::OnMouseEvent, this); Bind(wxEVT_LEFT_UP, &VideoDisplay::OnMouseEvent, this);
Bind(wxEVT_MOTION, &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); Bind(wxEVT_MOUSEWHEEL, &VideoDisplay::OnMouseWheel, this);
SetCursor(wxNullCursor); SetCursor(wxNullCursor);
if (con->videoController->IsLoaded()) { if (con->videoController->IsLoaded())
con->videoController->GetScriptSize(scriptW, scriptH);
OnVideoOpen(); OnVideoOpen();
}
} }
VideoDisplay::~VideoDisplay () { VideoDisplay::~VideoDisplay () {
@ -163,25 +154,6 @@ bool VideoDisplay::InitContext() {
return true; 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) { void VideoDisplay::UploadFrameData(FrameReadyEvent &evt) {
if (!InitContext()) return; if (!InitContext()) return;
@ -207,16 +179,10 @@ void VideoDisplay::UploadFrameData(FrameReadyEvent &evt) {
void VideoDisplay::OnVideoOpen() { void VideoDisplay::OnVideoOpen() {
if (!con->videoController->IsLoaded()) return; if (!con->videoController->IsLoaded()) return;
if (!tool.get())
cmd::call("video/tool/cross", con);
UpdateSize(); UpdateSize();
if (!tool.get()) tool.reset(new VisualToolCross(this, con, video, toolBar)); con->videoController->JumpToFrame(0);
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();
} }
void VideoDisplay::Render() try { void VideoDisplay::Render() try {
@ -225,7 +191,7 @@ void VideoDisplay::Render() try {
assert(wxIsMainThread()); assert(wxIsMainThread());
if (!viewport_height || !viewport_width) UpdateSize(); 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(glViewport(0, std::min(viewport_bottom, 0), w, h));
E(glMatrixMode(GL_PROJECTION)); 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 // Based on BBC's guidelines: http://www.bbc.co.uk/guidelines/dq/pdf/tv/tv_standards_london.pdf
// 16:9 or wider // 16:9 or wider
if (ar > 1.75) { if (ar > 1.75) {
DrawOverscanMask(w * 0.1, h * 0.05, wxColor(30,70,200),0.5); DrawOverscanMask(.1f, .05f);
DrawOverscanMask(w * 0.035, h * 0.035, wxColor(30,70,200),0.5); DrawOverscanMask(0.035f, 0.035f);
} }
// Less wide than 16:9 (use 4:3 standard) // Less wide than 16:9 (use 4:3 standard)
else { else {
DrawOverscanMask(w * 0.067, h * 0.05, wxColor(30,70,200),0.5); DrawOverscanMask(.067f, .05f);
DrawOverscanMask(w * 0.033, h * 0.035, wxColor(30,70,200),0.5); 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()) if (!con->videoController->IsPlaying())
tool->Draw(); tool->Draw();
} }
SwapBuffers(); SwapBuffers();
} }
catch (const VideoOutException &err) { catch (const agi::Exception &err) {
wxLogError( wxLogError(
"An error occurred trying to render the video frame on the screen.\n" "An error occurred trying to render the video frame on the screen.\n"
"Error message reported: %s", "Error message reported: %s",
err.GetMessage()); err.GetChainedMessage());
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.");
con->videoController->Reset(); 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 rad1 = h * 0.05;
int gapH = sizeH+rad1; Vector2D gap = size + rad1;
int gapV = sizeV+rad1; int rad2 = gap.Len() + 1;
int rad2 = sqrt(double(gapH*gapH + gapV*gapV)) + 1; Vector2D v(w, h);
Vector2D igap = v - gap;
Vector2D isize = v - size;
OpenGLWrapper gl; OpenGLWrapper gl;
E(gl.SetFillColour(color, alpha)); gl.SetFillColour(wxColor(30, 70, 200), .5f);
gl.SetLineColour(wxColor(0, 0, 0), 0.0, 1); gl.SetLineColour(*wxBLACK, 0, 1);
// Draw sides // Draw sides
E(gl.DrawRectangle(gapH, 0, w-gapH, sizeV)); // Top gl.DrawRectangle(Vector2D(gap, 0), Vector2D(igap, size)); // Top
E(gl.DrawRectangle(w-sizeH, gapV, w, h-gapV)); // Right gl.DrawRectangle(Vector2D(isize, gap), Vector2D(v, igap)); // Right
E(gl.DrawRectangle(gapH, h-sizeV, w-gapH, h)); // Bottom gl.DrawRectangle(Vector2D(gap, isize), Vector2D(igap, v)); // Bottom
E(gl.DrawRectangle(0, gapV, sizeH, h-gapV)); // Left gl.DrawRectangle(Vector2D(0, gap), Vector2D(size, igap)); // Left
// Draw rounded corners // Draw rounded corners
E(gl.DrawRing(gapH, gapV, rad1, rad2, 1.0, 180.0, 270.0)); // Top-left gl.DrawRing(gap, rad1, rad2, 1.f, 90.f, 180.f); // Top-left
E(gl.DrawRing(w-gapH, gapV, rad1, rad2, 1.0, 90.0, 180.0)); // Top-right gl.DrawRing(Vector2D(igap, gap), rad1, rad2, 1.f, 0.f, 90.f); // Top-right
E(gl.DrawRing(w-gapH, h-gapV, rad1, rad2, 1.0, 0.0, 90.0)); // Bottom-right gl.DrawRing(v - gap, rad1, rad2, 1.f, 270.f, 360.f); // Bottom-right
E(gl.DrawRing(gapH, h-gapV, rad1, rad2, 1.0,270.0,360.0)); // Bottom-left gl.DrawRing(Vector2D(gap, igap), rad1, rad2, 1.f, 180.f, 270.f); // Bottom-left
E(glDisable(GL_BLEND)); E(glDisable(GL_BLEND));
} }
@ -323,7 +271,7 @@ void VideoDisplay::UpdateSize(int arType, double arValue) {
if (freeSize) { if (freeSize) {
GetClientSize(&w,&h); GetClientSize(&w,&h);
viewport_x = 0; viewport_left = 0;
viewport_bottom = 0; viewport_bottom = 0;
viewport_top = 0; viewport_top = 0;
viewport_width = w; viewport_width = w;
@ -336,7 +284,7 @@ void VideoDisplay::UpdateSize(int arType, double arValue) {
// Window is wider than video, blackbox left/right // Window is wider than video, blackbox left/right
if (displayAr - videoAr > 0.01f) { if (displayAr - videoAr > 0.01f) {
int delta = w - videoAr * h; int delta = w - videoAr * h;
viewport_x = delta / 2; viewport_left = delta / 2;
viewport_width = w - delta; viewport_width = w - delta;
} }
@ -359,7 +307,7 @@ void VideoDisplay::UpdateSize(int arType, double arValue) {
// Cap the canvas size to the window size // Cap the canvas size to the window size
int cw = std::min(w, maxW), ch = std::min(h, maxH); int cw = std::min(w, maxW), ch = std::min(h, maxH);
viewport_x = 0; viewport_left = 0;
viewport_bottom = ch - h; viewport_bottom = ch - h;
viewport_top = 0; viewport_top = 0;
viewport_width = w; viewport_width = w;
@ -380,11 +328,8 @@ void VideoDisplay::UpdateSize(int arType, double arValue) {
SetEvtHandlerEnabled(true); SetEvtHandlerEnabled(true);
} }
con->videoController->GetScriptSize(scriptW, scriptH); if (tool.get())
video.w = w; tool->SetDisplayArea(viewport_left, viewport_top, viewport_width, viewport_height);
video.h = h;
if (tool.get()) tool->Refresh();
Refresh(false); Refresh(false);
} }
@ -403,20 +348,13 @@ void VideoDisplay::OnMouseEvent(wxMouseEvent& event) {
if (event.ButtonDown()) if (event.ButtonDown())
SetFocus(); SetFocus();
video.x = event.GetX(); mouse_pos = event.GetPosition();
video.y = event.GetY();
tool->OnMouseEvent(event); tool->OnMouseEvent(event);
} }
void VideoDisplay::OnMouseEnter(wxMouseEvent& event) {
ShowCursor(activeMode != Video_Mode_Standard);
tool->OnMouseEvent(event);
}
void VideoDisplay::OnMouseLeave(wxMouseEvent& event) { void VideoDisplay::OnMouseLeave(wxMouseEvent& event) {
video.x = INT_MIN; mouse_pos = Vector2D::Bad();
video.y = INT_MIN;
tool->OnMouseEvent(event); tool->OnMouseEvent(event);
} }
@ -429,33 +367,22 @@ void VideoDisplay::OnMouseWheel(wxMouseEvent& event) {
void VideoDisplay::OnContextMenu(wxContextMenuEvent&) { void VideoDisplay::OnContextMenu(wxContextMenuEvent&) {
if (!context_menu.get()) context_menu.reset(menu::GetMenu("video_context", con)); if (!context_menu.get()) context_menu.reset(menu::GetMenu("video_context", con));
ShowCursor(true); SetCursor(wxNullCursor);
menu::OpenPopupMenu(context_menu.get(), this); menu::OpenPopupMenu(context_menu.get(), this);
} }
void VideoDisplay::OnKeyDown(wxKeyEvent &event) { void VideoDisplay::OnKeyDown(wxKeyEvent &event) {
/// @todo event.StopPropagation();
int kc = event.GetKeyCode(); if (hotkey::check("Video", con, event.GetKeyCode(), event.GetUnicodeKey(), event.GetModifiers()))
if (kc == 'A') SetMode(Video_Mode_Standard); return;
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;
}
} }
void VideoDisplay::SetZoom(double value) { void VideoDisplay::SetZoom(double value) {
zoomValue = std::max(value, .125); zoomValue = std::max(value, .125);
zoomBox->SetValue(wxString::Format("%g%%", zoomValue * 100.)); zoomBox->SetValue(wxString::Format("%g%%", zoomValue * 100.));
UpdateSize(); UpdateSize();
} }
void VideoDisplay::SetZoomFromBox(wxCommandEvent &) { void VideoDisplay::SetZoomFromBox(wxCommandEvent &) {
wxString strValue = zoomBox->GetValue(); wxString strValue = zoomBox->GetValue();
strValue.EndsWith("%", &strValue); strValue.EndsWith("%", &strValue);
@ -466,57 +393,19 @@ void VideoDisplay::SetZoomFromBox(wxCommandEvent &) {
} }
} }
template<class T> void VideoDisplay::SetTool(VisualToolBase *new_tool) {
void VideoDisplay::SetTool() {
tool.reset();
tool.reset(new T(this, con, video, toolBar));
box->Bind(wxEVT_COMMAND_TOOL_CLICKED, &T::OnSubTool, static_cast<T*>(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;
toolBar->ClearTools(); toolBar->ClearTools();
toolBar->Realize(); toolBar->Realize();
toolBar->Show(false); toolBar->Show(false);
if (!box->visualToolBar->GetToolState(mode)) { tool.reset(new_tool);
box->visualToolBar->ToggleTool(mode, true); tool->SetToolbar(toolBar);
} tool->SetDisplayArea(viewport_left, viewport_top, viewport_width, viewport_height);
activeMode = mode;
switch (mode) {
case Video_Mode_Standard: SetTool<VisualToolCross>(); break;
case Video_Mode_Drag: SetTool<VisualToolDrag>(); break;
case Video_Mode_Rotate_Z: SetTool<VisualToolRotateZ>(); break;
case Video_Mode_Rotate_XY: SetTool<VisualToolRotateXY>(); break;
case Video_Mode_Scale: SetTool<VisualToolScale>(); break;
case Video_Mode_Clip: SetTool<VisualToolClip>(); break;
case Video_Mode_Vector_Clip: SetTool<VisualToolVectorClip>(); break;
default: assert(false); break;
}
// Update size as the new typesetting tool may have changed the subtoolbar size // Update size as the new typesetting tool may have changed the subtoolbar size
UpdateSize(); UpdateSize();
ShowCursor(activeMode != Video_Mode_Standard);
} }
void VideoDisplay::ToScriptCoords(int *x, int *y) const { Vector2D VideoDisplay::GetMousePosition() const {
int sx = *x - viewport_x > 0 ? viewport_width : -viewport_width; return mouse_pos ? tool->ToScriptCoords(mouse_pos) : mouse_pos;
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;
} }

View file

@ -44,12 +44,14 @@
#include <libaegisub/scoped_ptr.h> #include <libaegisub/scoped_ptr.h>
#include <libaegisub/signal.h> #include <libaegisub/signal.h>
#include "vector2d.h"
// Prototypes // Prototypes
class FrameReadyEvent; class FrameReadyEvent;
class VideoBox; class VideoBox;
class VideoContext; class VideoContext;
class VideoOutGL; class VideoOutGL;
class IVisualTool; class VisualToolBase;
class wxComboBox; class wxComboBox;
class wxTextCtrl; class wxTextCtrl;
class wxToolBar; class wxToolBar;
@ -59,14 +61,6 @@ namespace agi {
class OptionValue; 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 /// @class VideoDisplay
/// @brief DOCME /// @brief DOCME
class VideoDisplay : public wxGLCanvas { class VideoDisplay : public wxGLCanvas {
@ -87,8 +81,10 @@ class VideoDisplay : public wxGLCanvas {
/// The height of the canvas in screen pixels /// The height of the canvas in screen pixels
int h; int h;
Vector2D mouse_pos;
/// Screen pixels between the left of the canvas and the left of the video /// 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 /// The width of the video in screen pixels
int viewport_width; int viewport_width;
/// Screen pixels between the bottom of the canvas and the bottom of the video; used for glViewport /// 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<VideoOutGL> videoOut; agi::scoped_ptr<VideoOutGL> videoOut;
/// The active visual typesetting tool /// The active visual typesetting tool
agi::scoped_ptr<IVisualTool> tool; agi::scoped_ptr<VisualToolBase> tool;
/// The current tool's ID
int activeMode;
/// The toolbar used by individual typesetting tools /// The toolbar used by individual typesetting tools
wxToolBar* toolBar; wxToolBar* toolBar;
/// The OpenGL context for this display /// The OpenGL context for this display
agi::scoped_ptr<wxGLContext> glContext; agi::scoped_ptr<wxGLContext> glContext;
/// The current script width
int scriptW;
/// The current script height
int scriptH;
VideoState video;
/// The dropdown box for selecting zoom levels /// The dropdown box for selecting zoom levels
wxComboBox *zoomBox; wxComboBox *zoomBox;
@ -131,11 +118,9 @@ class VideoDisplay : public wxGLCanvas {
bool freeSize; bool freeSize;
/// @brief Draw an overscan mask /// @brief Draw an overscan mask
/// @param sizeH The amount of horizontal overscan on one side /// @param horizontal_percent The percent of the video reserved horizontally
/// @param sizeV The amount of vertical overscan on one side /// @param vertical_percent The percent of the video reserved vertically
/// @param colour The color of the mask void DrawOverscanMask(float horizontal_percent, float vertical_percent) const;
/// @param alpha The alpha of the mask
void DrawOverscanMask(int sizeH, int sizeV, wxColor color, double alpha) const;
/// Upload the image for the current frame to the video card /// Upload the image for the current frame to the video card
void UploadFrameData(FrameReadyEvent&); void UploadFrameData(FrameReadyEvent&);
@ -144,21 +129,8 @@ class VideoDisplay : public wxGLCanvas {
/// @return Could the context be set? /// @return Could the context be set?
bool InitContext(); bool InitContext();
/// @brief Set this video display to the given frame
/// @frameNumber The desired frame number
void SetFrame(int frameNumber);
void OnVideoOpen(); 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 <class T> 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 /// @brief Set the size of the display based on the current zoom and video resolution
void UpdateSize(int arType = -1, double arValue = -1.); void UpdateSize(int arType = -1, double arValue = -1.);
/// @brief Set the zoom level to that indicated by the dropdown /// @brief Set the zoom level to that indicated by the dropdown
@ -169,11 +141,9 @@ class VideoDisplay : public wxGLCanvas {
/// @brief Mouse event handler /// @brief Mouse event handler
void OnMouseEvent(wxMouseEvent& event); void OnMouseEvent(wxMouseEvent& event);
void OnMouseWheel(wxMouseEvent& event); void OnMouseWheel(wxMouseEvent& event);
void OnMouseEnter(wxMouseEvent& event);
void OnMouseLeave(wxMouseEvent& event); void OnMouseLeave(wxMouseEvent& event);
/// @brief Recalculate video positioning and scaling when the available area or zoom changes /// @brief Recalculate video positioning and scaling when the available area or zoom changes
void OnSizeEvent(wxSizeEvent &event); void OnSizeEvent(wxSizeEvent &event);
void OnMode(const wxCommandEvent &event);
void OnContextMenu(wxContextMenuEvent&); void OnContextMenu(wxContextMenuEvent&);
public: public:
@ -195,17 +165,8 @@ public:
/// @brief Get the current zoom level /// @brief Get the current zoom level
double GetZoom() const { return zoomValue; } double GetZoom() const { return zoomValue; }
/// @brief Convert a point from screen to script coordinate frame /// Get the last seen position of the mouse in script coordinates
/// @param x x coordinate; in/out Vector2D GetMousePosition() const;
/// @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 screen coordinates void SetTool(VisualToolBase *new_tool);
/// @param[out] x x coordinate
/// @param[out] y y coordinate
void GetMousePosition(int *x, int *y) const;
}; };

View file

@ -40,69 +40,89 @@
#include "visual_feature.h" #include "visual_feature.h"
VisualDraggableFeature::VisualDraggableFeature() VisualDraggableFeature::VisualDraggableFeature()
: type(DRAG_NONE) : start(Vector2D::Bad())
, x(INT_MIN) , type(DRAG_NONE)
, y(INT_MIN) , pos(Vector2D::Bad())
, origX(INT_MIN)
, origY(INT_MIN)
, layer(0) , 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) { switch (type) {
case DRAG_BIG_SQUARE: case DRAG_BIG_SQUARE:
return !(mx < x-8 || mx > x+8 || my < y-8 || my > y+8); return fabs(delta.X()) < 8 && fabs(delta.Y()) < 8;
case DRAG_BIG_CIRCLE: {
int dx = mx-x; case DRAG_BIG_CIRCLE:
int dy = my-y; return delta.SquareLen() < 64;
return dx*dx + dy*dy <= 64;
}
case DRAG_BIG_TRIANGLE: { case DRAG_BIG_TRIANGLE: {
int _my = my+2; if (delta.Y() < -10 || delta.Y() > 6) return false;
if (_my < y-8 || _my > y+8) return false; float dy = delta.Y() - 6;
int dx = mx-x; return 16 * delta.X() + 9 * dy < 0 && 16 * delta.X() - 9 * dy > 0;
int dy = _my-y-8;
return (16*dx+9*dy < 0 && 16*dx-9*dy > 0);
} }
case DRAG_SMALL_SQUARE: case DRAG_SMALL_SQUARE:
return !(mx < x-4 || mx > x+4 || my < y-4 || my > y+4); return fabs(delta.X()) < 4 && fabs(delta.Y()) < 4;
case DRAG_SMALL_CIRCLE: {
int dx = mx-x; case DRAG_SMALL_CIRCLE:
int dy = my-y; return delta.SquareLen() < 16;
return dx*dx + dy*dy <= 16;
}
default: default:
return false; return false;
} }
} }
void VisualDraggableFeature::Draw(OpenGLWrapper const& gl) const { void VisualDraggableFeature::Draw(OpenGLWrapper const& gl) const {
if (!pos) return;
switch (type) { switch (type) {
case DRAG_BIG_SQUARE: case DRAG_BIG_SQUARE:
gl.DrawRectangle(x-8,y-8,x+8,y+8); gl.DrawRectangle(pos - 8, pos + 8);
gl.DrawLine(x,y-16,x,y+16); gl.DrawLine(pos - Vector2D(0, 16), pos + Vector2D(0, 16));
gl.DrawLine(x-16,y,x+16,y); gl.DrawLine(pos - Vector2D(16, 0), pos + Vector2D(16, 0));
break; break;
case DRAG_BIG_CIRCLE: case DRAG_BIG_CIRCLE:
gl.DrawCircle(x,y,8); gl.DrawCircle(pos, 8);
gl.DrawLine(x,y-16,x,y+16); gl.DrawLine(pos - Vector2D(0, 16), pos + Vector2D(0, 16));
gl.DrawLine(x-16,y,x+16,y); gl.DrawLine(pos - Vector2D(16, 0), pos + Vector2D(16, 0));
break; break;
case DRAG_BIG_TRIANGLE: case DRAG_BIG_TRIANGLE:
gl.DrawTriangle(x-9,y-6,x+9,y-6,x,y+10); gl.DrawTriangle(pos - Vector2D(9, 6), pos + Vector2D(9, -6), pos + Vector2D(0, 10));
gl.DrawLine(x,y,x,y-16); gl.DrawLine(pos, pos + Vector2D(0, -16));
gl.DrawLine(x,y,x-14,y+8); gl.DrawLine(pos, pos + Vector2D(-14, 8));
gl.DrawLine(x,y,x+14,y+8); gl.DrawLine(pos, pos + Vector2D(14, 8));
break; break;
case DRAG_SMALL_SQUARE: case DRAG_SMALL_SQUARE:
gl.DrawRectangle(x-4,y-4,x+4,y+4); gl.DrawRectangle(pos - 4, pos + 4);
break; break;
case DRAG_SMALL_CIRCLE: case DRAG_SMALL_CIRCLE:
gl.DrawCircle(x,y,4); gl.DrawCircle(pos, 4);
break; break;
default: default:
break; 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;
}

View file

@ -36,6 +36,8 @@
#pragma once #pragma once
#include "vector2d.h"
class OpenGLWrapper; class OpenGLWrapper;
class AssDialogue; class AssDialogue;
@ -65,6 +67,8 @@ enum DraggableFeatureType {
/// @class VisualDraggableFeature /// @class VisualDraggableFeature
/// @brief Onscreen control used by many visual tools which doesn't do much /// @brief Onscreen control used by many visual tools which doesn't do much
class VisualDraggableFeature { class VisualDraggableFeature {
Vector2D start; ///< position before the last operation began
public: public:
/// @brief Constructor /// @brief Constructor
VisualDraggableFeature(); VisualDraggableFeature();
@ -72,21 +76,23 @@ public:
/// Shape of feature /// Shape of feature
DraggableFeatureType type; DraggableFeatureType type;
int x; /// x coordinate Vector2D pos;
int y; /// y coordinate
int origX; /// x coordindate before the last operation began
int origY; /// y coordindate before the last operation began
int layer; /// Layer; Higher = above int layer; /// Layer; Higher = above
AssDialogue* line; /// The dialogue line this feature is for AssDialogue* line; /// The dialogue line this feature is for
/// @brief Is the given point over this feature? /// @brief Is the given point over this feature?
/// @param mx x coordinate to test /// @param mouse_pos Position of the mouse
/// @param my y coordinate to test bool IsMouseOver(Vector2D mouse_pos) const;
bool IsMouseOver(int x,int y) const;
/// @brief Draw this feature /// @brief Draw this feature
/// @param gl OpenGLWrapper to use /// @param gl OpenGLWrapper to use
void Draw(OpenGLWrapper const& gl) const; void Draw(OpenGLWrapper const& gl) const;
void StartDrag();
void UpdateDrag(Vector2D d, bool single_axis);
bool HasMoved() const;
}; };

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -40,6 +27,8 @@
#include <wx/glcanvas.h> #include <wx/glcanvas.h>
#endif #endif
#include "visual_tool.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"
#include "ass_override.h" #include "ass_override.h"
@ -47,133 +36,219 @@
#include "ass_time.h" #include "ass_time.h"
#include "include/aegisub/context.h" #include "include/aegisub/context.h"
#include "main.h" #include "main.h"
#include "subs_grid.h"
#include "utils.h" #include "utils.h"
#include "video_context.h" #include "video_context.h"
#include "video_display.h" #include "video_display.h"
#include "video_provider_manager.h" #include "video_provider_manager.h"
#include "visual_feature.h" #include "visual_feature.h"
#include "visual_tool.h"
#include "visual_tool_clip.h" #include "visual_tool_clip.h"
#include "visual_tool_drag.h" #include "visual_tool_drag.h"
#include "visual_tool_vector_clip.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<class C, class F>
static void for_each(C &range, F func) {
std::for_each(range.begin(), range.end(), func);
}
template<class FeatureType> using std::tr1::placeholders::_1;
VisualTool<FeatureType>::VisualTool(VideoDisplay *parent, agi::Context *context, VideoState const& video)
: dragStartX(0) const wxColour VisualToolBase::colour[4] = {wxColour(106,32,19), wxColour(255,169,40), wxColour(255,253,185), wxColour(187,0,0)};
, dragStartY(0)
, commitId(-1) VisualToolBase::VisualToolBase(VideoDisplay *parent, agi::Context *context)
, selChanged(false) : c(context)
, selectedFeatures(selFeatures)
, c(context)
, parent(parent) , parent(parent)
, holding(false) , holding(false)
, active_line(0)
, dragging(false) , dragging(false)
, externalChange(true) , frame_number(c->videoController->GetFrameN())
, video(video) , left_click(false)
, leftClick(false) , left_double(false)
, leftDClick(false) , shift_down(false)
, shiftDown(false) , ctrl_down(false)
, ctrlDown(false) , alt_down(false)
, altDown(false) , file_changed_connection(c->ass->AddCommitListener(&VisualToolBase::OnCommit, this))
, commit_id(-1)
{ {
frameNumber = c->videoController->GetFrameN(); int script_w, script_h;
curDiag = GetActiveDialogueLine(); c->ass->GetResolution(script_w, script_h);
script_res = Vector2D(script_w, script_h);
active_line = GetActiveDialogueLine();
c->selectionController->AddSelectionListener(this); 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<class FeatureType> template<class FeatureType>
VisualTool<FeatureType>::~VisualTool() { VisualTool<FeatureType>::VisualTool(VideoDisplay *parent, agi::Context *context)
c->selectionController->RemoveSelectionListener(this); : VisualToolBase(parent, context)
, sel_changed(false)
{
active_feature = features.begin();
} }
template<class FeatureType> template<class FeatureType>
void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) { void VisualTool<FeatureType>::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()) { if (event.Leaving()) {
Update(); mouse_pos = Vector2D::Bad();
parent->Render(); parent->Render();
return; return;
} }
else if (event.Entering() && !OPT_GET("Tool/Visual/Always Show")->GetBool()) {
needRender = true;
}
externalChange = false;
leftClick = event.ButtonDown(wxMOUSE_BTN_LEFT); if (event.Entering() && !OPT_GET("Tool/Visual/Always Show")->GetBool())
leftDClick = event.LeftDClick(); need_render = true;
shiftDown = event.m_shiftDown;
#ifdef __APPLE__
ctrlDown = event.m_metaDown; // Cmd key
#else
ctrlDown = event.m_controlDown;
#endif
altDown = event.m_altDown;
if (!dragging) { if (!dragging) {
feature_iterator oldHigh = curFeature; feature_iterator prev_feature = active_feature;
GetHighlightedFeature();
if (curFeature != oldHigh) needRender = true; 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) { if (dragging) {
// continue drag // continue drag
if (event.LeftIsDown()) { if (event.LeftIsDown()) {
for (selection_iterator cur = selFeatures.begin(); cur != selFeatures.end(); ++cur) { for_each(sel_features, bind(&FeatureType::UpdateDrag, _1,
(*cur)->x = (video.x - dragStartX + (*cur)->origX); mouse_pos - drag_start, shift_down));
(*cur)->y = (video.y - dragStartY + (*cur)->origY); for_each(sel_features, bind(&VisualTool<FeatureType>::UpdateDrag, this, _1));
if (shiftDown) {
if (abs(video.x - dragStartX) > abs(video.y - dragStartY)) {
(*cur)->y = (*cur)->origY;
}
else {
(*cur)->x = (*cur)->origX;
}
}
UpdateDrag(*cur);
CommitDrag(*cur);
}
Commit(); Commit();
needRender = true; need_render = true;
} }
// end drag // end drag
else { else {
dragging = false; dragging = false;
// mouse didn't move, fiddle with selection // mouse didn't move, fiddle with selection
if (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 // Don't deselect stuff that was selected in this click's mousedown event
if (!selChanged) { if (!sel_changed) {
if (ctrlDown) { if (ctrl_down)
// deselect this feature RemoveSelection(active_feature);
RemoveSelection(curFeature); else
} SetSelection(active_feature, true);
else {
SetSelection(curFeature);
}
} }
} }
else {
for (selection_iterator cur = selFeatures.begin(); cur != selFeatures.end(); ++cur) {
CommitDrag(*cur);
}
Commit();
}
curFeature = features.end(); active_feature = features.end();
parent->ReleaseMouse(); parent->ReleaseMouse();
parent->SetFocus(); parent->SetFocus();
} }
} }
else if (holding) { else if (holding) {
// continue hold
if (event.LeftIsDown()) { if (event.LeftIsDown()) {
UpdateHold(); UpdateHold();
needRender = true; need_render = true;
} }
// end hold // end hold
else { else {
@ -182,422 +257,318 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
parent->ReleaseMouse(); parent->ReleaseMouse();
parent->SetFocus(); parent->SetFocus();
} }
CommitHold();
Commit(); Commit();
} }
else if (leftClick) { else if (left_click) {
drag_start = mouse_pos;
// start drag // start drag
if (curFeature != features.end()) { if (active_feature != features.end()) {
if (selFeatures.find(curFeature) == selFeatures.end()) { if (!sel_features.count(active_feature)) {
selChanged = true; sel_changed = true;
if (ctrlDown) { SetSelection(active_feature, !ctrl_down);
AddSelection(curFeature);
}
else {
SetSelection(curFeature);
}
} }
else { else
selChanged = false; sel_changed = false;
}
if (curFeature->line) c->selectionController->SetActiveLine(curFeature->line);
if (InitializeDrag(curFeature)) { if (active_feature->line)
dragStartX = video.x; c->selectionController->SetActiveLine(active_feature->line);
dragStartY = video.y;
for (selection_iterator cur = selFeatures.begin(); cur != selFeatures.end(); ++cur) {
(*cur)->origX = (*cur)->x;
(*cur)->origY = (*cur)->y;
}
if (InitializeDrag(active_feature)) {
for_each(sel_features, bind(&VisualDraggableFeature::StartDrag, _1));
dragging = true; dragging = true;
parent->CaptureMouse(); parent->CaptureMouse();
} }
} }
// start hold // start hold
else { else {
if (!altDown) { if (!alt_down) {
ClearSelection(); sel_features.clear();
Selection sel; Selection sel;
sel.insert(c->selectionController->GetActiveLine()); sel.insert(c->selectionController->GetActiveLine());
c->selectionController->SetSelectedSet(sel); c->selectionController->SetSelectedSet(sel);
needRender = true; need_render = true;
} }
if (curDiag && InitializeHold()) { if (active_line && InitializeHold()) {
holding = true; holding = true;
parent->CaptureMouse(); parent->CaptureMouse();
} }
} }
} }
if (Update() || needRender) parent->Render(); if (active_line && left_double)
externalChange = true; OnDoubleClick();
if (!event.LeftIsDown()) { //if (need_render)
// Only coalesce the changes made in a single drag parent->Render();
commitId = -1;
}
}
template<class FeatureType> // Only coalesce the changes made in a single drag
void VisualTool<FeatureType>::Commit(wxString message) { if (!event.LeftIsDown())
externalChange = false; commit_id = -1;
if (message.empty()) {
message = _("visual typesetting");
}
commitId = c->ass->Commit(message, AssFile::COMMIT_DIAG_TEXT, commitId);
externalChange = true;
}
template<class FeatureType>
AssDialogue* VisualTool<FeatureType>::GetActiveDialogueLine() {
AssDialogue *diag = c->selectionController->GetActiveLine();
if (diag && c->subsGrid->IsDisplayed(diag))
return diag;
return NULL;
}
template<class FeatureType>
void VisualTool<FeatureType>::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;
}
}
} }
template<class FeatureType> template<class FeatureType>
void VisualTool<FeatureType>::DrawAllFeatures() { void VisualTool<FeatureType>::DrawAllFeatures() {
SetLineColour(colour[0],1.0f,2); gl.SetLineColour(colour[0], 1.0f, 2);
for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) { for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) {
int fill; int fill = 1;
if (cur == curFeature) if (cur == active_feature)
fill = 2; fill = 2;
else if (selFeatures.find(cur) != selFeatures.end()) else if (sel_features.count(cur))
fill = 3; fill = 3;
else gl.SetFillColour(colour[fill], 0.6f);
fill = 1; cur->Draw(gl);
SetFillColour(colour[fill],0.6f);
cur->Draw(*this);
} }
} }
template<class FeatureType> template<class FeatureType>
void VisualTool<FeatureType>::Refresh() { void VisualTool<FeatureType>::SetSelection(feature_iterator feat, bool clear) {
if (externalChange) { if (clear)
curDiag = GetActiveDialogueLine(); sel_features.clear();
curFeature = features.end();
OnFileChanged();
}
}
template<class FeatureType>
void VisualTool<FeatureType>::SetFrame(int newFrameNumber) {
if (frameNumber == newFrameNumber) return;
frameNumber = newFrameNumber;
curFeature = features.end();
OnFrameChanged();
AssDialogue *newCurDiag = GetActiveDialogueLine();
if (newCurDiag != curDiag) {
curDiag = newCurDiag;
OnLineChanged();
}
}
template<class FeatureType>
void VisualTool<FeatureType>::OnActiveLineChanged(AssDialogue *new_line) {
if (new_line && !c->subsGrid->IsDisplayed(new_line)) {
new_line = NULL;
}
if (new_line != curDiag) {
curDiag = new_line;
OnLineChanged();
}
}
template<class FeatureType>
void VisualTool<FeatureType>::SetSelection(feature_iterator feat) {
selFeatures.clear();
lineSelCount.clear();
selFeatures.insert(feat);
AssDialogue *line = feat->line;
if (line) {
lineSelCount[line] = 1;
if (sel_features.insert(feat).second && feat->line) {
Selection sel; Selection sel;
sel.insert(line); if (!clear)
c->selectionController->SetSelectedSet(sel); sel = c->selectionController->GetSelectedSet();
} if (sel.insert(feat->line).second)
}
template<class FeatureType>
void VisualTool<FeatureType>::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) {
c->selectionController->SetSelectedSet(sel); c->selectionController->SetSelectedSet(sel);
}
} }
} }
template<class FeatureType> template<class FeatureType>
void VisualTool<FeatureType>::RemoveSelection(feature_iterator feat) { void VisualTool<FeatureType>::RemoveSelection(feature_iterator feat) {
if (selFeatures.erase(feat) > 0 && feat->line) { if (!sel_features.erase(feat) || !feat->line) return;
// 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();
// Don't deselect the only selected line for (selection_iterator it = sel_features.begin(); it != sel_features.end(); ++it) {
if (sel.size() <= 1) return; if ((*it)->line == feat->line) 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);
}
} }
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<class FeatureType> //////// PARSERS
void VisualTool<FeatureType>::ClearSelection() {
selFeatures.clear();
lineSelCount.clear();
}
enum TagFoundType { typedef const std::vector<AssOverrideParameter*> * param_vec;
TAG_NOT_FOUND = 0,
PRIMARY_TAG_FOUND, // Parse line on creation and unparse at the end of scope
ALT_TAG_FOUND 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 // Find a tag's parameters in a line or return NULL if it's not found
/// @param line Line to get the value from static param_vec find_tag(const AssDialogue *line, wxString tag_name) {
/// @param tag Tag to get the value of for (size_t i = 0; i < line->Blocks.size(); ++i) {
/// @param n Number of parameters passed
/// @return Which tag (if any) was found
template<class T>
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++) {
const AssDialogueBlockOverride *ovr = dynamic_cast<const AssDialogueBlockOverride*>(line->Blocks[i]); const AssDialogueBlockOverride *ovr = dynamic_cast<const AssDialogueBlockOverride*>(line->Blocks[i]);
if (!ovr) continue; if (!ovr) continue;
for (size_t j=0; j < ovr->Tags.size(); j++) { for (size_t j = 0; j < ovr->Tags.size(); ++j) {
const AssOverrideTag *cur = ovr->Tags[j]; if (ovr->Tags[j]->Name == tag_name)
if ((cur->Name == tag || cur->Name == alt) && cur->Params.size() >= n) { return &ovr->Tags[j]->Params;
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<T>(*val);
}
va_end(argp);
return cur->Name == alt ? ALT_TAG_FOUND : PRIMARY_TAG_FOUND;
}
} }
} }
return TAG_NOT_FOUND;
return 0;
} }
template<class FeatureType> // Get a Vector2D from the given tag parameters, or Vector2D::Bad() if they are not valid
void VisualTool<FeatureType>::GetLinePosition(AssDialogue *diag,int &x, int &y) { static Vector2D vec_or_bad(param_vec tag, size_t x_idx, size_t y_idx) {
int orgx,orgy; if (!tag ||
GetLinePosition(diag,x,y,orgx,orgy); 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<float>(), (*tag)[y_idx]->Get<float>());
} }
template<class FeatureType> Vector2D VisualToolBase::GetLinePosition(AssDialogue *diag) {
void VisualTool<FeatureType>::GetLinePosition(AssDialogue *diag,int &x, int &y, int &orgx, int &orgy) { 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]; 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; int align = 2;
AssStyle *style = c->ass->GetStyle(diag->Style); if (AssStyle *style = c->ass->GetStyle(diag->Style)) {
if (style) {
align = style->alignment; align = style->alignment;
for (int i=0;i<4;i++) { for (int i = 0; i < 4; i++) {
if (margin[i] == 0) margin[i] = style->Margin[i]; if (margin[i] == 0)
margin[i] = style->Margin[i];
} }
} }
int sw,sh; param_vec align_tag;
c->videoController->GetScriptSize(sw,sh); if ((align_tag = find_tag(diag, "\\an")) && !(*align_tag)[0]->omitted)
align = (*align_tag)[0]->Get<int>();
else if ((align_tag = find_tag(diag, "\\a"))) {
align = (*align_tag)[0]->Get<int>(2);
// Process margins // \a -> \an values mapping
margin[1] = sw - margin[1]; static int align_mapping[] = { 2, 1, 2, 3, 7, 7, 8, 9, 7, 4, 5, 6 };
margin[3] = sh - margin[2]; if (static_cast<size_t>(align) < sizeof(align_mapping) / sizeof(int))
align = align_mapping[align];
// Overrides processing else
diag->ParseASSTags(); align = 2;
if (!get_value<int>(diag, "\\pos", 2, &x, &y)) {
if (get_value<int>(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];
} }
parent->FromScriptCoords(&x, &y); // Alignment type
int hor = (align - 1) % 3;
int vert = (align - 1) / 3;
if (!get_value<int>(diag, "\\org", 2, &orgx, &orgy)) { // Calculate positions
orgx = x; int x, y;
orgy = y; if (hor == 0)
} x = margin[0];
else { else if (hor == 1)
parent->FromScriptCoords(&orgx, &orgy); 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<class FeatureType> Vector2D VisualToolBase::GetLineOrigin(AssDialogue *diag) {
void VisualTool<FeatureType>::GetLineMove(AssDialogue *diag,bool &hasMove,int &x1,int &y1,int &x2,int &y2,int &t1,int &t2) { scoped_tag_parse parse(diag);
diag->ParseASSTags(); return vec_or_bad(find_tag(diag, "\\org"), 0, 1);
hasMove =
get_value<int>(diag, "\\move", 6, &x1, &y1, &x2, &y2, &t1, &t2) ||
get_value<int>(diag, "\\move", 4, &x1, &y1, &x2, &y2);
if (hasMove) {
parent->FromScriptCoords(&x1, &y1);
parent->FromScriptCoords(&x2, &y2);
}
diag->ClearBlocks();
} }
template<class FeatureType> bool VisualToolBase::GetLineMove(AssDialogue *diag, Vector2D &p1, Vector2D &p2, int &t1, int &t2) {
void VisualTool<FeatureType>::GetLineRotation(AssDialogue *diag,float &rx,float &ry,float &rz) { 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<int>(0);
t2 = (*tag)[5]->Get<int>(0);
return p1 && p2;
}
void VisualToolBase::GetLineRotation(AssDialogue *diag, float &rx, float &ry, float &rz) {
rx = ry = rz = 0.f; rx = ry = rz = 0.f;
AssStyle *style = c->ass->GetStyle(diag->Style); if (AssStyle *style = c->ass->GetStyle(diag->Style))
if (style) {
rz = style->angle; rz = style->angle;
}
diag->ParseASSTags(); scoped_tag_parse parse(diag);
get_value<float>(diag, "\\frx", 1, &rx); if (param_vec tag = find_tag(diag, "\\frx"))
get_value<float>(diag, "\\fry", 1, &ry); rx = tag->front()->Get<float>(rx);
get_value<float>(diag, "\\frz", 1, &rz); if (param_vec tag = find_tag(diag, "\\fry"))
ry = tag->front()->Get<float>(ry);
diag->ClearBlocks(); if (param_vec tag = find_tag(diag, "\\frz"))
rz = tag->front()->Get<float>(rz);
else if (param_vec tag = find_tag(diag, "\\fr"))
rz = tag->front()->Get<float>(rz);
} }
template<class FeatureType> void VisualToolBase::GetLineScale(AssDialogue *diag, Vector2D &scale) {
void VisualTool<FeatureType>::GetLineScale(AssDialogue *diag,float &scalX,float &scalY) { float x = 100.f, y = 100.f;
scalX = scalY = 100.f;
AssStyle *style = c->ass->GetStyle(diag->Style); if (AssStyle *style = c->ass->GetStyle(diag->Style)) {
if (style) { x = style->scalex;
scalX = style->scalex; y = style->scaley;
scalY = style->scaley;
} }
diag->ParseASSTags(); scoped_tag_parse parse(diag);
get_value<float>(diag, "\\fscx", 1, &scalX); if (param_vec tag = find_tag(diag, "\\fscx"))
get_value<float>(diag, "\\fscy", 1, &scalY); x = tag->front()->Get<float>(x);
if (param_vec tag = find_tag(diag, "\\fscy"))
y = tag->front()->Get<float>(y);
diag->ClearBlocks(); scale = Vector2D(x, y);
} }
template<class FeatureType> void VisualToolBase::GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse) {
void VisualTool<FeatureType>::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;
inverse = false; inverse = false;
diag->ParseASSTags(); scoped_tag_parse parse(diag);
inverse = get_value<int>(diag, "\\clip", 4, &x1, &y1, &x2, &y2) == ALT_TAG_FOUND; param_vec tag = find_tag(diag, "\\iclip");
diag->ClearBlocks(); if (tag)
inverse = true;
else
tag = find_tag(diag, "\\clip");
parent->FromScriptCoords(&x1, &y1); if (tag && tag->size() == 4) {
parent->FromScriptCoords(&x2, &y2); p1 = vec_or_bad(tag, 0, 1);
p2 = vec_or_bad(tag, 2, 3);
}
else {
p1 = Vector2D();
p2 = script_res - 1;
}
} }
template<class FeatureType> wxString VisualToolBase::GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse) {
wxString VisualTool<FeatureType>::GetLineVectorClip(AssDialogue *diag,int &scale,bool &inverse) { scoped_tag_parse parse(diag);
scale = 1; scale = 1;
inverse = false; inverse = false;
diag->ParseASSTags();
int x1, y1, x2, y2; param_vec tag = find_tag(diag, "\\iclip");
TagFoundType res = get_value<int>(diag, "\\clip", 4, &x1, &y1, &x2, &y2); if (tag)
if (res) { inverse = true;
inverse = res == ALT_TAG_FOUND; else
diag->ClearBlocks(); tag = find_tag(diag, "\\clip");
return wxString::Format("m %d %d l %d %d %d %d %d %d", x1, y1, x2, y1, x2, y2, x1, y2);
if (tag && tag->size() == 4) {
return wxString::Format("m %d %d l %d %d %d %d %d %d",
(*tag)[0]->Get<int>(), (*tag)[1]->Get<int>(),
(*tag)[2]->Get<int>(), (*tag)[1]->Get<int>(),
(*tag)[2]->Get<int>(), (*tag)[3]->Get<int>(),
(*tag)[0]->Get<int>(), (*tag)[3]->Get<int>());
} }
wxString result; if (tag) {
wxString scaleStr; scale = (*tag)[0]->Get<int>(scale);
res = get_value<wxString>(diag, "\\clip", 2, &scaleStr, &result); return (*tag)[1]->Get<wxString>("");
inverse = res == ALT_TAG_FOUND;
if (!scaleStr.empty()) {
long s;
scaleStr.ToLong(&s);
scale = s;
} }
diag->ClearBlocks();
return result; return "";
} }
/// @brief Set override void VisualToolBase::SetSelectedOverride(wxString const& tag, wxString const& value) {
/// @param tag for_each(c->selectionController->GetSelectedSet(),
/// @param value bind(&VisualToolBase::SetOverride, this, _1, tag, value));
template<class FeatureType> }
void VisualTool<FeatureType>::SetOverride(AssDialogue* line, wxString tag, wxString value) {
void VisualToolBase::SetOverride(AssDialogue* line, wxString const& tag, wxString const& value) {
if (!line) return; if (!line) return;
wxString removeTag; wxString removeTag;
if (tag == "\\1c") removeTag = "\\c"; 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 == "\\pos") removeTag = "\\move";
else if (tag == "\\move") removeTag = "\\pos"; else if (tag == "\\move") removeTag = "\\pos";
else if (tag == "\\clip") removeTag = "\\iclip"; else if (tag == "\\clip") removeTag = "\\iclip";
@ -607,17 +578,14 @@ void VisualTool<FeatureType>::SetOverride(AssDialogue* line, wxString tag, wxStr
// Get block at start // Get block at start
line->ParseASSTags(); line->ParseASSTags();
AssDialogueBlock *block = line->Blocks.at(0); AssDialogueBlock *block = line->Blocks.front();
// Get current block as plain or override // Get current block as plain or override
AssDialogueBlockPlain *plain = dynamic_cast<AssDialogueBlockPlain*>(block);
AssDialogueBlockOverride *ovr = dynamic_cast<AssDialogueBlockOverride*>(block);
assert(dynamic_cast<AssDialogueBlockDrawing*>(block) == NULL); assert(dynamic_cast<AssDialogueBlockDrawing*>(block) == NULL);
if (plain) { if (dynamic_cast<AssDialogueBlockPlain*>(block))
line->Text = "{" + insert + "}" + line->Text; line->Text = "{" + insert + "}" + line->Text;
} else if (AssDialogueBlockOverride *ovr = dynamic_cast<AssDialogueBlockOverride*>(block)) {
else if (ovr) {
// Remove old of same // Remove old of same
for (size_t i = 0; i < ovr->Tags.size(); i++) { for (size_t i = 0; i < ovr->Tags.size(); i++) {
wxString name = ovr->Tags[i]->Name; wxString name = ovr->Tags[i]->Name;
@ -631,8 +599,6 @@ void VisualTool<FeatureType>::SetOverride(AssDialogue* line, wxString tag, wxStr
line->UpdateText(); line->UpdateText();
} }
parent->SetFocus();
} }
// If only export worked // If only export worked

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -36,6 +23,7 @@
#pragma once #pragma once
#ifndef AGI_PRE #ifndef AGI_PRE
#include <deque>
#include <map> #include <map>
#include <set> #include <set>
#include <vector> #include <vector>
@ -45,83 +33,158 @@
#include <wx/button.h> #include <wx/button.h>
#endif #endif
#include "base_grid.h" #include <libaegisub/signal.h>
#include "gl_wrap.h" #include "gl_wrap.h"
#include "selection_controller.h"
#include "vector2d.h"
class AssDialogue; class AssDialogue;
class SubtitlesGrid; class SubtitlesGrid;
class VideoDisplay; class VideoDisplay;
struct VideoState; class wxToolBar;
namespace agi { namespace agi {
struct Context; struct Context;
class OptionValue; class OptionValue;
} }
/// First window id for visualsubtoolbar items /// @class VisualToolBase
#define VISUAL_SUB_TOOL_START 1300 /// @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 VisualTool<T>s 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<AssDialogue> {
std::deque<agi::signal::Connection> connections;
/// Last window id for visualsubtoolbar items void OnCommit(int type);
#define VISUAL_SUB_TOOL_END (VISUAL_SUB_TOOL_START+100) void OnSeek(int new_frame);
class IVisualTool : public OpenGLWrapper { void OnMouseCaptureLost(wxMouseCaptureLostEvent &);
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<class T>
bool operator()(T lft, T rgt) const {
return &*lft < &*rgt;
}
};
/// DOCME
/// @class VisualTool
/// @brief DOCME
/// DOCME
template<class FeatureType>
class VisualTool : public IVisualTool, protected SubtitleSelectionListener {
protected:
typedef FeatureType Feature;
typedef typename std::list<FeatureType>::iterator feature_iterator;
typedef typename std::list<FeatureType>::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();
/// @brief Get the dialogue line currently in the edit box /// @brief Get the dialogue line currently in the edit box
/// @return NULL if the line is not active on the current frame /// @return NULL if the line is not active on the current frame
AssDialogue *GetActiveDialogueLine(); 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 FeatureType>
class VisualTool : public VisualToolBase {
protected:
typedef FeatureType Feature;
typedef typename std::list<FeatureType>::iterator feature_iterator;
typedef typename std::list<FeatureType>::const_iterator feature_const_iterator;
private:
struct ltaddr {
template<class T>
bool operator()(T lft, T rgt) const {
return &*lft < &*rgt;
}
};
std::list<agi::signal::Connection> slots;
typedef typename std::set<feature_iterator, ltaddr>::iterator selection_iterator; typedef typename std::set<feature_iterator, ltaddr>::iterator selection_iterator;
std::set<feature_iterator, ltaddr> selFeatures; /// Currently selected visual features bool sel_changed; /// Has the selection already been changed in the current click?
std::map<AssDialogue*, int> lineSelCount; /// Number of selected features for each line
bool selChanged; /// Has the selection already been changed in the current click?
/// @brief Called when a hold is begun /// @brief Called when a hold is begun
/// @return Should the hold actually happen? /// @return Should the hold actually happen?
virtual bool InitializeHold() { return false; } virtual bool InitializeHold() { return false; }
/// @brief Called on every mouse event during a hold /// @brief Called on every mouse event during a hold
virtual void UpdateHold() { } virtual void UpdateHold() { }
/// @brief Called at the end of a hold
virtual void CommitHold() { }
/// @brief Called at the beginning of a drag /// @brief Called at the beginning of a drag
/// @param feature The visual feature clicked on /// @param feature The visual feature clicked on
@ -130,67 +193,22 @@ private:
/// @brief Called on every mouse event during a drag /// @brief Called on every mouse event during a drag
/// @param feature The current feature to process; not necessarily the one clicked on /// @param feature The current feature to process; not necessarily the one clicked on
virtual void UpdateDrag(feature_iterator feature) { } virtual void UpdateDrag(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 /// @brief Draw stuff
virtual void Draw()=0; virtual void Draw()=0;
protected: protected:
/// Read-only reference to the set of selected features for subclasses std::set<feature_iterator, ltaddr> sel_features; ///< Currently selected visual features
const std::set<feature_iterator, ltaddr> &selectedFeatures;
typedef typename std::set<feature_iterator, ltaddr>::const_iterator sel_iterator; typedef typename std::set<feature_iterator, ltaddr>::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 /// Topmost feature under the mouse; generally only valid during a drag
std::list<FeatureType> features; /// List of features which are drawn and can be clicked on feature_iterator active_feature;
/// List of features which are drawn and can be clicked on
int frameNumber; /// Current frame number /// List is used here for the iterator invalidation properties
VideoState const& video; /// Mouse and video information std::list<FeatureType> features;
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);
/// Draw all of the features in the list /// Draw all of the features in the list
void DrawAllFeatures(); 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 /// @brief Remove a feature from the selection
/// @param i Index in the feature list /// @param i Index in the feature list
@ -199,35 +217,15 @@ protected:
/// @brief Set the selection to a single feature, deselecting everything else /// @brief Set the selection to a single feature, deselecting everything else
/// @param i Index in the feature list /// @param i Index in the feature list
void SetSelection(feature_iterator feat); void SetSelection(feature_iterator feat, bool clear);
/// @brief Clear the selection
void ClearSelection();
// SubtitleSelectionListener implementation
void OnActiveLineChanged(AssDialogue *new_line);
virtual void OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed) { }
public: public:
/// @brief Handler for all mouse events /// @brief Handler for all mouse events
/// @param event Shockingly enough, the mouse event /// @param event Shockingly enough, the mouse event
void OnMouseEvent(wxMouseEvent &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 /// @brief Constructor
/// @param parent The VideoDisplay to use for coordinate conversion /// @param parent The VideoDisplay to use for coordinate conversion
/// @param video Video and mouse information passing blob /// @param video Video and mouse information passing blob
VisualTool(VideoDisplay *parent, agi::Context *context, VideoState const& video); VisualTool(VideoDisplay *parent, agi::Context *context);
/// @brief Destructor
virtual ~VisualTool();
}; };

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -39,35 +26,30 @@
#include <utility> #include <utility>
#endif #endif
#include "ass_dialogue.h"
#include "ass_file.h"
#include "utils.h"
#include "video_display.h"
#include "visual_tool_clip.h" #include "visual_tool_clip.h"
VisualToolClip::VisualToolClip(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *) #include "utils.h"
: VisualTool<ClipCorner>(parent, context, video)
, curX1(0) VisualToolClip::VisualToolClip(VideoDisplay *parent, agi::Context *context)
, curY1(0) : VisualTool<ClipCorner>(parent, context)
, curX2(video.w) , cur_1(0, 0)
, curY2(video.h) , cur_2(video_res)
, inverse(false) , inverse(false)
{ {
if (curDiag) {
GetLineClip(curDiag,curX1,curY1,curX2,curY2,inverse);
}
Feature feat; Feature feat;
feat.type = DRAG_SMALL_CIRCLE; feat.type = DRAG_SMALL_CIRCLE;
features.resize(4, feat); 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(); feature_iterator cur = features.begin();
feats[0] = &*(cur++); feats[0] = &*(cur++);
feats[1] = &*(cur++); feats[1] = &*(cur++);
feats[2] = &*(cur++); feats[2] = &*(cur++);
feats[3] = &*(cur++); feats[3] = &*(cur++);
// Attach each feature to the two features it shares edges with
// Top-left // Top-left
int i = 0; int i = 0;
feats[i]->horiz = feats[1]; feats[i]->horiz = feats[1];
@ -87,129 +69,81 @@ VisualToolClip::VisualToolClip(VideoDisplay *parent, agi::Context *context, Vide
// Bottom-right // Bottom-right
feats[i]->horiz = feats[2]; feats[i]->horiz = feats[2];
feats[i]->vert = feats[1]; feats[i]->vert = feats[1];
i++;
} }
void VisualToolClip::Draw() { void VisualToolClip::Draw() {
if (!curDiag) return; if (!active_line) return;
int dx1 = curX1; DrawAllFeatures();
int dy1 = curY1;
int dx2 = curX2;
int dy2 = curY2;
// Draw rectangle // Draw rectangle
SetLineColour(colour[3],1.0f,2); gl.SetLineColour(colour[3], 1.0f, 2);
SetFillColour(colour[3],0.0f); gl.SetFillColour(colour[3], 0.0f);
DrawRectangle(dx1,dy1,dx2,dy2); gl.DrawRectangle(cur_1, cur_2);
// Draw outside area // Draw outside area
SetLineColour(colour[3],0.0f); gl.SetLineColour(colour[3], 0.0f);
SetFillColour(wxColour(0,0,0),0.5f); gl.SetFillColour(*wxBLACK, 0.5f);
if (inverse) { if (inverse) {
DrawRectangle(dx1,dy1,dx2,dy2); gl.DrawRectangle(cur_1, cur_2);
} }
else { else {
DrawRectangle(0,0,video.w,dy1); Vector2D p1 = cur_1.Min(cur_2);
DrawRectangle(0,dy2,video.w,video.h); Vector2D p2 = cur_1.Max(cur_2);
DrawRectangle(0,dy1,dx1,dy2); gl.DrawRectangle(Vector2D(0, 0), Vector2D(video_res, p1));
DrawRectangle(dx2,dy1,video.w,dy2); 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() { bool VisualToolClip::InitializeHold() {
startX = video.x;
startY = video.y;
curDiag->StripTag("\\clip");
curDiag->StripTag("\\iclip");
return true; return true;
} }
void VisualToolClip::UpdateHold() { 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 // Limit to video area
curX1 = mid(0,curX1,video.w); Vector2D zero(0, 0);
curX2 = mid(0,curX2,video.w); cur_1 = zero.Max(video_res.Min(drag_start));
curY1 = mid(0,curY1,video.h); cur_2 = zero.Max(video_res.Min(mouse_pos));
curY2 = mid(0,curY2,video.h);
SetFeaturePositions(); SetFeaturePositions();
CommitHold();
} }
void VisualToolClip::CommitHold() { void VisualToolClip::CommitHold() {
int x1 = curX1; SetOverride(active_line, inverse ? "\\iclip" : "\\clip",
int x2 = curX2; wxString::Format("(%s,%s)", ToScriptCoords(cur_1.Min(cur_2)).Str(), ToScriptCoords(cur_1.Max(cur_2)).Str()));
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));
} }
bool VisualToolClip::InitializeDrag(feature_iterator) { bool VisualToolClip::InitializeDrag(feature_iterator) {
curDiag->StripTag("\\clip");
curDiag->StripTag("\\iclip");
return true; return true;
} }
void VisualToolClip::UpdateDrag(feature_iterator feature) { void VisualToolClip::UpdateDrag(feature_iterator feature) {
// Update brothers // Update features which share an edge with the dragged one
feature->horiz->y = feature->y; feature->horiz->pos = Vector2D(feature->horiz->pos, feature->pos);
feature->vert->x = feature->x; feature->vert->pos = Vector2D(feature->pos, feature->vert->pos);
// Get "cur" from features cur_1 = features.front().pos;
curX1 = feats[0]->x; cur_2 = features.back().pos;
curX2 = feats[3]->x;
curY1 = feats[0]->y;
curY2 = feats[3]->y;
// Make sure p1 < p2
if (curX1 > curX2) std::swap(curX1,curX2);
if (curY1 > curY2) std::swap(curY1,curY2);
}
void VisualToolClip::CommitDrag(feature_iterator) {
CommitHold(); CommitHold();
} }
void VisualToolClip::SetFeaturePositions() { void VisualToolClip::SetFeaturePositions() {
// Top-left feature_iterator it = features.begin();
int i = 0; (it++)->pos = cur_1; // Top-left
feats[i]->x = curX1; (it++)->pos = Vector2D(cur_2, cur_1); // Top-right
feats[i]->y = curY1; (it++)->pos = Vector2D(cur_1, cur_2); // Bottom-left
i++; it->pos = cur_2; // Bottom-right
// 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;
} }
void VisualToolClip::DoRefresh() { void VisualToolClip::DoRefresh() {
if (curDiag) { if (active_line) {
GetLineClip(curDiag,curX1,curY1,curX2,curY2,inverse); GetLineClip(active_line, cur_1, cur_2, inverse);
cur_1 = FromScriptCoords(cur_1);
cur_2 = FromScriptCoords(cur_2);
SetFeaturePositions(); SetFeaturePositions();
} }
} }

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -39,16 +26,10 @@
/// @class ClipCorner /// @class ClipCorner
/// @brief VisualDraggableFeature with siblings /// @brief VisualDraggableFeature with siblings
class ClipCorner : public VisualDraggableFeature { struct ClipCorner : public VisualDraggableFeature {
public: ClipCorner *horiz; /// Other corner on this corner's horizontal line
ClipCorner* horiz; /// Other corner on this corner's horizontal line ClipCorner *vert; /// Other corner on this corner's vertical line
ClipCorner* vert; /// Other corner on this corner's vertical line ClipCorner() : VisualDraggableFeature() , horiz(0) , vert(0) { }
/// @brief Constructor
ClipCorner()
: VisualDraggableFeature()
, horiz(NULL)
, vert(NULL)
{ }
}; };
/// DOCME /// DOCME
@ -57,11 +38,10 @@ public:
/// ///
/// DOCME /// DOCME
class VisualToolClip : public VisualTool<ClipCorner> { class VisualToolClip : public VisualTool<ClipCorner> {
int startX,startY,curX1,curY1,curX2,curY2; Vector2D cur_1;
Vector2D cur_2;
ClipCorner *feats[4]; bool inverse; ///< Is this currently in iclip mode?
bool inverse;
bool InitializeHold(); bool InitializeHold();
void UpdateHold(); void UpdateHold();
@ -72,9 +52,8 @@ class VisualToolClip : public VisualTool<ClipCorner> {
bool InitializeDrag(feature_iterator feature); bool InitializeDrag(feature_iterator feature);
void UpdateDrag(feature_iterator feature); void UpdateDrag(feature_iterator feature);
void CommitDrag(feature_iterator feature);
void Draw(); void Draw();
public: public:
VisualToolClip(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *); VisualToolClip(VideoDisplay *parent, agi::Context *context);
}; };

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -35,88 +22,81 @@
#include "config.h" #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" #include "visual_tool_cross.h"
VisualToolCross::VisualToolCross(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *) #include "gl_text.h"
: VisualTool<VisualDraggableFeature>(parent, context, video) #include "include/aegisub/context.h"
, glText(new OpenGLText) #include "video_display.h"
VisualToolCross::VisualToolCross(VideoDisplay *parent, agi::Context *context)
: VisualTool<VisualDraggableFeature>(parent, context)
, gl_text(new OpenGLText)
{ {
parent->SetCursor(wxCursor(wxCURSOR_BLANK));
} }
bool VisualToolCross::Update() { VisualToolCross::~VisualToolCross() {
if (!leftDClick) return true; parent->SetCursor(wxNullCursor);
if (!curDiag) return true; }
int dx, dy; void VisualToolCross::OnDoubleClick() {
int vx = video.x; Vector2D d = ToScriptCoords(mouse_pos) - GetLinePosition(active_line);
int vy = video.y;
GetLinePosition(curDiag, dx, dy);
parent->ToScriptCoords(&vx, &vy);
parent->ToScriptCoords(&dx, &dy);
dx -= vx;
dy -= vy;
Selection sel = c->selectionController->GetSelectedSet(); Selection sel = c->selectionController->GetSelectedSet();
for (Selection::const_iterator cur = sel.begin(); cur != sel.end(); ++cur) { for (Selection::const_iterator it = sel.begin(); it != sel.end(); ++it) {
int x1, y1; Vector2D p1, p2;
GetLinePosition(*cur, x1, y1); int t1, t2;
parent->ToScriptCoords(&x1, &y1); if (GetLineMove(*it, p1, p2, t1, t2)) {
SetOverride(*cur, "\\pos", wxString::Format("(%i,%i)", x1 - dx, y1 - dy)); 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")); Commit(_("positioning"));
return false;
} }
void VisualToolCross::Draw() { void VisualToolCross::Draw() {
if (!mouse_pos) return;
// Draw cross // Draw cross
glDisable(GL_LINE_SMOOTH); gl.SetInvert();
glEnable(GL_COLOR_LOGIC_OP); gl.SetLineColour(*wxWHITE, 1.0, 1);
glLogicOp(GL_INVERT); float lines[] = {
glLineWidth(1); 0.f, mouse_pos.Y(),
glBegin(GL_LINES); video_res.X(), mouse_pos.Y(),
glColor3f(1.0f,1.0f,1.0f); mouse_pos.X(), 0.f,
glVertex2f(0, video.y); mouse_pos.X(), video_res.Y()
glVertex2f(video.w, video.y); };
glVertex2f(video.x, 0); gl.DrawLines(2, lines, 4);
glVertex2f(video.x, video.h); gl.ClearInvert();
glEnd();
glDisable(GL_COLOR_LOGIC_OP);
int tx,ty; Vector2D t = ToScriptCoords(shift_down ? video_res - mouse_pos : mouse_pos);
if (!wxGetMouseState().ShiftDown()) { wxString mouse_text = t.Str();
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);
int tw,th; int tw, th;
glText->SetFont("Verdana", 12, true, false); gl_text->SetFont("Verdana", 12, true, false);
glText->SetColour(wxColour(255, 255, 255), 1.f); gl_text->SetColour(*wxWHITE, 1.f);
glText->GetExtent(mouseText, tw, th); gl_text->GetExtent(mouse_text, tw, th);
// Calculate draw position // Place the text in the corner of the cross closest to the center of the video
int dx = video.x; int dx = mouse_pos.X();
int dy = video.y; int dy = mouse_pos.Y();
bool left = dx > video.w / 2; if (dx > video_res.X() / 2)
bool bottom = dy < video.h / 2; dx -= tw + 4;
else
dx += 4;
// Text draw coords if (dy < video_res.Y() / 2)
if (left) dx -= tw + 4; dy += 3;
else dx += 4; else
if (bottom) dy += 3; dy -= th + 3;
else dy -= th + 3;
// Draw text gl_text->Print(mouse_text, dx, dy);
glText->Print(mouseText, dx, dy);
} }

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -34,24 +21,21 @@
/// @ingroup visual_ts /// @ingroup visual_ts
/// ///
#ifndef AGI_PRE #include <libaegisub/scoped_ptr.h>
#include <tr1/memory>
#endif
#include "visual_feature.h" #include "visual_feature.h"
#include "visual_tool.h" #include "visual_tool.h"
class OpenGLText; class OpenGLText;
/// DOCME
/// @class VisualToolCross /// @class VisualToolCross
/// @brief DOCME /// @brief A crosshair which shows the current mouse position and on double-click
/// /// shifts the selected lines to the clicked point
/// DOCME
class VisualToolCross : public VisualTool<VisualDraggableFeature> { class VisualToolCross : public VisualTool<VisualDraggableFeature> {
bool Update(); void OnDoubleClick();
void Draw(); void Draw();
std::tr1::shared_ptr<OpenGLText> glText; agi::scoped_ptr<OpenGLText> gl_text;
public: public:
VisualToolCross(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *); VisualToolCross(VideoDisplay *parent, agi::Context *context);
~VisualToolCross();
}; };

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -35,83 +22,79 @@
#include "config.h" #include "config.h"
#include "visual_tool_drag.h"
#ifndef AGI_PRE
#include <wx/bmpbuttn.h>
#include <wx/toolbar.h>
#endif
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "ass_file.h" #include "ass_file.h"
#include "include/aegisub/context.h" #include "include/aegisub/context.h"
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "subs_grid.h"
#include "utils.h" #include "utils.h"
#include "video_context.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_ORIGIN = DRAG_BIG_TRIANGLE;
static const DraggableFeatureType DRAG_START = DRAG_BIG_SQUARE; static const DraggableFeatureType DRAG_START = DRAG_BIG_SQUARE;
static const DraggableFeatureType DRAG_END = DRAG_BIG_CIRCLE; static const DraggableFeatureType DRAG_END = DRAG_BIG_CIRCLE;
/// @brief Constructor VisualToolDrag::VisualToolDrag(VideoDisplay *parent, agi::Context *context)
/// @param _parent : VisualTool<VisualToolDragDraggableFeature>(parent, context)
/// @param toolBar , primary(0)
VisualToolDrag::VisualToolDrag(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar * toolBar) , button_is_move(true)
: VisualTool<VisualToolDragDraggableFeature>(parent, context, video)
, toolBar(toolBar)
, primary(NULL)
, toggleMoveOnMove(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); 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() { void VisualToolDrag::UpdateToggleButtons() {
// Check which bitmap to use bool to_move = true;
bool toMove = true; if (active_line) {
if (curDiag) { Vector2D p1, p2;
int x1,y1,x2,y2,t1,t2; int t1, t2;
bool hasMove; to_move = !GetLineMove(active_line, p1, p2, t1, t2);
GetLineMove(curDiag,hasMove,x1,y1,x2,y2,t1,t2);
toMove = !hasMove;
} }
// No change needed if (to_move == button_is_move) return;
if (toMove == toggleMoveOnMove) return;
// Change bitmap toolbar->SetToolNormalBitmap(toolbar->GetToolByPos(0)->GetId(),
if (toMove) { to_move ? GETIMAGE(visual_move_conv_move_24) : GETIMAGE(visual_move_conv_pos_24));
toolBar->SetToolNormalBitmap(BUTTON_TOGGLE_MOVE, GETIMAGE(visual_move_conv_move_24)); button_is_move = to_move;
}
else {
toolBar->SetToolNormalBitmap(BUTTON_TOGGLE_MOVE, GETIMAGE(visual_move_conv_pos_24));
}
toggleMoveOnMove = toMove;
} }
/// @brief Toggle button pressed
/// @param event
void VisualToolDrag::OnSubTool(wxCommandEvent &) { void VisualToolDrag::OnSubTool(wxCommandEvent &) {
// Toggle \move <-> \pos // Toggle \move <-> \pos
VideoContext *vc = c->videoController;
for (Selection::const_iterator cur = selection.begin(); cur != selection.end(); ++cur) { for (Selection::const_iterator cur = selection.begin(); cur != selection.end(); ++cur) {
AssDialogue *line = *cur; AssDialogue *line = *cur;
int x1,y1,x2,y2,t1,t2; Vector2D p1, p2;
bool hasMove; int t1, t2;
GetLinePosition(line,x1,y1); bool has_move = GetLineMove(line, p1, p2, t1, t2);
GetLineMove(line,hasMove,x1,y1,x2,y2,t1,t2);
parent->ToScriptCoords(&x1, &y1);
parent->ToScriptCoords(&x2, &y2);
if (hasMove) SetOverride(line, "\\pos",wxString::Format("(%i,%i)",x1,y1)); if (has_move)
else SetOverride(line, "\\move",wxString::Format("(%i,%i,%i,%i,%i,%i)",x1,y1,x1,y1,0,line->End.GetMS() - line->Start.GetMS())); 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(); Commit();
Refresh(); OnFileChanged();
UpdateToggleButtons(); UpdateToggleButtons();
} }
@ -122,161 +105,142 @@ void VisualToolDrag::OnLineChanged() {
void VisualToolDrag::OnFileChanged() { void VisualToolDrag::OnFileChanged() {
/// @todo it should be possible to preserve the selection in some cases /// @todo it should be possible to preserve the selection in some cases
features.clear(); features.clear();
ClearSelection(); sel_features.clear();
primary = NULL; primary = 0;
active_feature = features.end();
for (int i = c->subsGrid->GetRows() - 1; i >=0; i--) { for (entryIter it = c->ass->Line.begin(); it != c->ass->Line.end(); ++it) {
AssDialogue *diag = c->subsGrid->GetDialogue(i); AssDialogue *diag = dynamic_cast<AssDialogue*>(*it);
if (c->subsGrid->IsDisplayed(diag)) { if (diag && IsDisplayed(diag))
MakeFeatures(diag); MakeFeatures(diag);
}
} }
UpdateToggleButtons(); UpdateToggleButtons();
} }
void VisualToolDrag::OnFrameChanged() { 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 feat = features.begin();
feature_iterator end = features.end(); feature_iterator end = features.end();
for (int i = c->subsGrid->GetRows() - 1; i >=0; i--) { for (entryIter it = c->ass->Line.begin(); it != c->ass->Line.end(); ++it) {
AssDialogue *diag = c->subsGrid->GetDialogue(i); AssDialogue *diag = dynamic_cast<AssDialogue*>(*it);
if (c->subsGrid->IsDisplayed(diag)) { if (!diag) continue;
if (IsDisplayed(diag)) {
// Features don't exist and should // Features don't exist and should
if (feat == end || feat->line != diag) { if (feat == end || feat->line != diag)
MakeFeatures(diag, feat); MakeFeatures(diag, feat);
}
// Move past already existing features for the line // Move past already existing features for the line
else { else
while (feat != end && feat->line == diag) ++feat; while (feat != end && feat->line == diag) ++feat;
}
} }
else { else {
// Remove all features for this line (if any) // Remove all features for this line (if any)
while (feat != end && feat->line == diag) { while (feat != end && feat->line == diag) {
feat->line = NULL; feat->line = 0;
RemoveSelection(feat); RemoveSelection(feat);
feat = features.erase(feat); feat = features.erase(feat);
} }
} }
} }
active_feature = features.end();
} }
void VisualToolDrag::OnSelectedSetChanged(const Selection &added, const Selection &removed) { void VisualToolDrag::OnSelectedSetChanged(const Selection &added, const Selection &removed) {
c->selectionController->GetSelectedSet(selection); c->selectionController->GetSelectedSet(selection);
if (!externalChange) return;
externalChange = false;
c->subsGrid->BeginBatch();
for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) { for (feature_iterator it = features.begin(); it != features.end(); ++it) {
// Remove all deselected lines if (removed.count(it->line))
if (removed.find(cur->line) != removed.end()) { sel_features.erase(it);
RemoveSelection(cur); else if (added.count(it->line) && it->type == DRAG_START)
} sel_features.insert(it);
// And add all newly selected lines
else if (added.find(cur->line) != added.end() && cur->type == DRAG_START) {
AddSelection(cur);
}
} }
c->subsGrid->EndBatch();
externalChange = true;
} }
void VisualToolDrag::Draw() { void VisualToolDrag::Draw() {
DrawAllFeatures(); DrawAllFeatures();
// Draw arrows // Draw connecting lines
for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) { for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) {
if (cur->type == DRAG_START) continue; if (cur->type == DRAG_START) continue;
feature_iterator p2 = cur; feature_iterator p2 = cur;
feature_iterator p1 = cur->parent; feature_iterator p1 = cur->parent;
// Has arrow? // Move end marker has an arrow; origin doesn't
bool hasArrow = p2->type == DRAG_END; bool has_arrow = p2->type == DRAG_END;
int arrowLen = hasArrow ? 10 : 0; int arrow_len = has_arrow ? 10 : 0;
// See if the distance between them is enough // Don't show the connecting line if the features are very close
int dx = p2->x - p1->x; Vector2D direction = p2->pos - p1->pos;
int dy = p2->y - p1->y; if (direction.SquareLen() < (20 + arrow_len) * (20 + arrow_len)) continue;
int dist = (int)sqrt(double(dx*dx + dy*dy));
if (dist < 20+arrowLen) continue;
// Get end points direction = direction.Unit();
int x1 = p1->x + dx*10/dist; // Get the start and end points of the line
int x2 = p2->x - dx*(10+arrowLen)/dist; Vector2D start = p1->pos + direction * 10;
int y1 = p1->y + dy*10/dist; Vector2D end = p2->pos - direction * (10 + arrow_len);
int y2 = p2->y - dy*(10+arrowLen)/dist;
// Draw arrow if (has_arrow) {
if (hasArrow) { gl.SetLineColour(colour[3], 0.8f, 2);
// 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);
// Arrow line // Arrow line
SetLineColour(colour[3],0.8f,2); gl.DrawLine(start, end);
DrawLine(x1,y1,x2,y2);
// Arrow head // Arrow head
DrawLine(x2+sx,y2-sy,x2-sx,y2+sy); Vector2D t_half_base_w = Vector2D(-direction.Y(), direction.X()) * 4;
DrawLine(x2+sx,y2-sy,x2+dx*10/dist,y2+dy*10/dist); gl.DrawTriangle(end + direction * arrow_len, end + t_half_base_w, end - t_half_base_w);
DrawLine(x2-sx,y2+sy,x2+dx*10/dist,y2+dy*10/dist);
} }
// Draw dashed line // Draw dashed line
else { else {
SetLineColour(colour[3],0.5f,2); gl.SetLineColour(colour[3], 0.5f, 2);
DrawDashedLine(x1,y1,x2,y2,6); gl.DrawDashedLine(start, end, 6);
} }
} }
} }
void VisualToolDrag::MakeFeatures(AssDialogue *diag) { void VisualToolDrag::MakeFeatures(AssDialogue *diag) {
MakeFeatures(diag, features.end()); MakeFeatures(diag, features.end());
} }
void VisualToolDrag::MakeFeatures(AssDialogue *diag, feature_iterator pos) { void VisualToolDrag::MakeFeatures(AssDialogue *diag, feature_iterator pos) {
// Get position Vector2D p1 = FromScriptCoords(GetLinePosition(diag));
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);
// Create \pos feature // Create \pos feature
Feature feat; Feature feat;
feat.x = x1; feat.pos = p1;
feat.y = y1;
feat.layer = 0; feat.layer = 0;
feat.type = DRAG_START; feat.type = DRAG_START;
feat.time = t1; feat.time = 0;
feat.line = diag; feat.line = diag;
feat.parent = features.end(); feat.parent = features.end();
features.insert(pos, feat); features.insert(pos, feat);
feature_iterator cur = pos; --cur; feature_iterator cur = pos; --cur;
feat.parent = cur; feat.parent = cur;
if (selection.find(diag) != selection.end()) { if (selection.count(diag))
AddSelection(cur); sel_features.insert(cur);
}
Vector2D p2;
int t1, t2;
// Create move destination feature // Create move destination feature
if (hasMove) { if (GetLineMove(diag, p1, p2, t1, t2)) {
feat.x = x2; feat.pos = FromScriptCoords(p2);
feat.y = y2;
feat.layer = 1; feat.layer = 1;
feat.type = DRAG_END; feat.type = DRAG_END;
feat.parent->time = t1;
feat.time = t2; feat.time = t2;
feat.line = diag; feat.line = diag;
features.insert(pos, feat); features.insert(pos, feat);
feat.parent->parent = --pos; ++pos; feat.parent->parent = --pos; ++pos;
} }
// Create org feature // Create org feature
if (torgx != x1 || torgy != y1) { if (Vector2D org = GetLineOrigin(diag)) {
feat.x = torgx; feat.pos = FromScriptCoords(org);
feat.y = torgy;
feat.layer = -1; feat.layer = -1;
feat.type = DRAG_ORIGIN; feat.type = DRAG_ORIGIN;
feat.time = 0; 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 // Set time of clicked feature to the current frame and shift all other
// selected features by the same amount // selected features by the same amount
if (feature->type != DRAG_ORIGIN) { 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; int change = time - feature->time;
for (sel_iterator cur = selectedFeatures.begin(); cur != selectedFeatures.end(); ++cur) { for (sel_iterator cur = sel_features.begin(); cur != sel_features.end(); ++cur) {
if ((*cur)->type != DRAG_ORIGIN) { (*cur)->time += change;
(*cur)->time += change;
}
} }
} }
return true; return true;
} }
void VisualToolDrag::CommitDrag(feature_iterator feature) { void VisualToolDrag::UpdateDrag(feature_iterator feature) {
if (feature->type == DRAG_ORIGIN) { if (feature->type == DRAG_ORIGIN) {
int x = feature->x; SetOverride(feature->line, "\\org", ToScriptCoords(feature->pos).PStr());
int y = feature->y;
parent->ToScriptCoords(&x, &y);
SetOverride(feature->line, "\\org",wxString::Format("(%i,%i)",x,y));
return; return;
} }
feature_iterator p = feature->parent; feature_iterator end_feature = feature->parent;
if (feature->type == DRAG_END) { if (feature->type == DRAG_END)
std::swap(feature, p); std::swap(feature, end_feature);
}
int x1 = feature->x; if (feature->parent == features.end())
int y1 = feature->y; SetOverride(feature->line, "\\pos", ToScriptCoords(feature->pos).PStr());
parent->ToScriptCoords(&x1, &y1); else
SetOverride(feature->line, "\\move",
// Position wxString::Format("(%s,%s,%d,%d)",
if (feature->parent == features.end()) { ToScriptCoords(feature->pos).Str(),
SetOverride(feature->line, "\\pos", wxString::Format("(%i,%i)", x1, y1)); ToScriptCoords(end_feature->pos).Str(),
} feature->time, end_feature->time));
// 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));
}
} }
bool VisualToolDrag::Update() {
if (!leftDClick) return false;
int dx, dy; void VisualToolDrag::OnDoubleClick() {
int vx = video.x; Vector2D d = ToScriptCoords(mouse_pos) - (primary ? ToScriptCoords(primary->pos) : GetLinePosition(active_line));
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;
for (Selection::const_iterator cur = selection.begin(); cur != selection.end(); ++cur) { Selection sel = c->selectionController->GetSelectedSet();
int x1 = 0, y1 = 0, x2 = 0, y2 = 0, t1 = INT_MIN, t2 = INT_MIN, orgx, orgy; for (Selection::const_iterator it = sel.begin(); it != sel.end(); ++it) {
bool isMove; Vector2D p1, p2;
int t1, t2;
GetLinePosition(*cur, x1, y1, orgx, orgy); if (GetLineMove(*it, p1, p2, t1, t2)) {
GetLineMove(*cur, isMove, x1, y1, x2, y2, t1, t2); if (t1 > 0 || t2 > 0)
parent->ToScriptCoords(&x1, &y1); SetOverride(*it, "\\move", wxString::Format("(%s,%s,%d,%d)", (p1 + d).Str(), (p2 + d).Str(), t1, t2));
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));
else else
SetOverride(*cur, "\\move", wxString::Format("(%i,%i,%i,%i)", x1, y1, x2, y2)); SetOverride(*it, "\\move", wxString::Format("(%s,%s)", (p1 + d).Str(), (p2 + d).Str()));
}
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));
} }
else
SetOverride(*it, "\\pos", (GetLinePosition(*it) + d).PStr());
if (Vector2D org = GetLineOrigin(*it))
SetOverride(*it, "\\org", (org + d).PStr());
} }
Commit(_("positioning")); Commit(_("positioning"));
OnFileChanged(); OnFileChanged();
return false;
} }

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -34,11 +21,6 @@
/// @ingroup visual_ts /// @ingroup visual_ts
/// ///
#ifndef AGI_PRE
#include <wx/bmpbuttn.h>
#include <wx/toolbar.h>
#endif
#include "visual_feature.h" #include "visual_feature.h"
#include "visual_tool.h" #include "visual_tool.h"
@ -51,27 +33,26 @@ public:
VisualToolDragDraggableFeature() : VisualDraggableFeature(), time(0) { } VisualToolDragDraggableFeature() : VisualDraggableFeature(), time(0) { }
}; };
class wxBitmapButton;
class wxToolBar;
/// DOCME /// DOCME
/// @class VisualToolDrag /// @class VisualToolDrag
/// @brief DOCME /// @brief Moveable features for the positions of each visible line
///
/// DOCME
class VisualToolDrag : public VisualTool<VisualToolDragDraggableFeature> { class VisualToolDrag : public VisualTool<VisualToolDragDraggableFeature> {
/// The subtoolbar for the move/pos conversion button /// The subtoolbar for the move/pos conversion button
wxToolBar *toolBar; wxToolBar *toolbar;
/// The feature last clicked on for the double-click handler /// The feature last clicked on for the double-click handler
/// Equal to curFeature during drags; possibly different at all other times /// 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 /// longer exists
Feature *primary; Feature *primary;
/// The last announced selection set /// The last announced selection set
Selection selection; Selection selection;
int change;
/// When the button is pressed, will it convert the line to a move (vs. from /// 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 /// 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 /// @brief Create the features for a line
/// @param diag Line to create the features for /// @param diag Line to create the features for
@ -79,23 +60,23 @@ class VisualToolDrag : public VisualTool<VisualToolDragDraggableFeature> {
void MakeFeatures(AssDialogue *diag, feature_iterator pos); void MakeFeatures(AssDialogue *diag, feature_iterator pos);
void MakeFeatures(AssDialogue *diag); 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<> // Overriding SubtitleSelectionListener inherited from base VisualTool<>
void OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed); void OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed);
void OnFrameChanged(); void OnFrameChanged();
void OnFileChanged(); void OnFileChanged();
void OnLineChanged(); void OnLineChanged();
void OnCoordinateSystemsChanged() { OnFileChanged(); }
bool InitializeDrag(feature_iterator feature);
void UpdateDrag(feature_iterator feature);
void Draw(); void Draw();
bool Update(); void OnDoubleClick();
public:
VisualToolDrag(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *toolbar);
/// Set the pos/move button to the correct icon based on the active line
void UpdateToggleButtons();
void OnSubTool(wxCommandEvent &event); void OnSubTool(wxCommandEvent &event);
public:
VisualToolDrag(VideoDisplay *parent, agi::Context *context);
void SetToolbar(wxToolBar *tb);
}; };

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -36,175 +23,152 @@
#include "config.h" #include "config.h"
#ifndef AGI_PRE #ifndef AGI_PRE
#include <math.h> #include <cmath>
#endif #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" #include "visual_tool_rotatexy.h"
VisualToolRotateXY::VisualToolRotateXY(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *) VisualToolRotateXY::VisualToolRotateXY(VideoDisplay *parent, agi::Context *context)
: VisualTool<VisualDraggableFeature>(parent, context, video) : VisualTool<VisualDraggableFeature>(parent, context)
{ {
features.resize(1); features.resize(1);
org = &features.back(); org = &features.back();
org->type = DRAG_BIG_TRIANGLE; org->type = DRAG_BIG_TRIANGLE;
DoRefresh();
} }
void VisualToolRotateXY::Draw() { 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(); DrawAllFeatures();
// Transform grid // Transform grid
glMatrixMode(GL_MODELVIEW); gl.SetOrigin(org->pos);
glPushMatrix(); gl.SetRotation(angle_x, angle_y, angle_z);
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);
// Draw grid // Draw grid
glShadeModel(GL_SMOOTH); gl.SetLineColour(colour[0], 0.5f, 2);
SetLineColour(colour[0],0.5f,2); gl.SetModeLine();
SetModeLine(); float r = colour[0].Red() / 255.f;
float r = colour[0].Red()/255.f; float g = colour[0].Green() / 255.f;
float g = colour[0].Green()/255.f; float b = colour[0].Blue() / 255.f;
float b = colour[0].Blue()/255.f;
glBegin(GL_LINES); std::vector<float> colors(11 * 8 * 4);
for (int i=0;i<11;i++) { for (int i = 0; i < 88; ++i) {
float a = 1.f - abs(i-5)*0.18f; colors[i * 4 + 0] = r;
int pos = 20*(i-5); colors[i * 4 + 1] = g;
glColor4f(r,g,b,0.f); colors[i * 4 + 2] = b;
glVertex2i(pos,120); colors[i * 4 + 3] = (i + 3) % 4 > 1 ? 0 : (1.f - abs(i / 8 - 5) * 0.18f);
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);
} }
glEnd();
std::vector<float> 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 // Draw vectors
SetLineColour(colour[3],1.f,2); gl.SetLineColour(colour[3], 1.f, 2);
SetModeLine(); float vectors[] = {
glBegin(GL_LINES); 0.f, 0.f, 0.f,
glVertex3f(0.f,0.f,0.f); 50.f, 0.f, 0.f,
glVertex3f(50.f,0.f,0.f); 0.f, 0.f, 0.f,
glVertex3f(0.f,0.f,0.f); 0.f, 50.f, 0.f,
glVertex3f(0.f,50.f,0.f); 0.f, 0.f, 0.f,
glVertex3f(0.f,0.f,0.f); 0.f, 0.f, 50.f,
glVertex3f(0.f,0.f,50.f); };
glEnd(); gl.DrawLines(3, vectors, 6);
// Draw arrow tops // Draw arrow tops
glBegin(GL_TRIANGLE_FAN); float arrows[] = {
glVertex3f(60.f,0.f,0.f); 60.f, 0.f, 0.f,
glVertex3f(50.f,-3.f,-3.f); 50.f, -3.f, -3.f,
glVertex3f(50.f,3.f,-3.f); 50.f, 3.f, -3.f,
glVertex3f(50.f,3.f,3.f); 50.f, 3.f, 3.f,
glVertex3f(50.f,-3.f,3.f); 50.f, -3.f, 3.f,
glVertex3f(50.f,-3.f,-3.f); 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();
glPopMatrix(); 0.f, 60.f, 0.f,
glShadeModel(GL_FLAT); -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() { bool VisualToolRotateXY::InitializeHold() {
startAngleX = (org->y-video.y)*2.f; orig_x = angle_x;
startAngleY = (video.x-org->x)*2.f; orig_y = angle_y;
origAngleX = curAngleX;
origAngleY = curAngleY;
return true; return true;
} }
void VisualToolRotateXY::UpdateHold() { void VisualToolRotateXY::UpdateHold() {
float screenAngleX = (org->y-video.y)*2.f; Vector2D delta = (mouse_pos - drag_start) * 2;
float screenAngleY = (video.x-org->x)*2.f; if (shift_down)
delta = delta.SingleAxis();
// Deltas angle_x = orig_x - delta.Y();
float deltaX = screenAngleX - startAngleX; angle_y = orig_y + delta.X();
float deltaY = screenAngleY - startAngleY;
if (shiftDown) { if (ctrl_down) {
if (fabs(deltaX) >= fabs(deltaY)) deltaY = 0.f; angle_x = floorf(angle_x / 30.f + .5f) * 30.f;
else deltaX = 0.f; angle_y = floorf(angle_y / 30.f + .5f) * 30.f;
} }
// Calculate angle_x = fmodf(angle_x + 360.f, 360.f);
curAngleX = fmodf(deltaX + origAngleX + 360.f, 360.f); angle_y = fmodf(angle_y + 360.f, 360.f);
curAngleY = fmodf(deltaY + origAngleY + 360.f, 360.f);
// Oh Snap SetSelectedOverride("\\frx", wxString::Format("(%0.3g)", angle_x));
if (ctrlDown) { SetSelectedOverride("\\fry", wxString::Format("(%0.3g)", angle_y));
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;
}
} }
void VisualToolRotateXY::CommitHold() { void VisualToolRotateXY::UpdateDrag(feature_iterator feature) {
Selection sel = c->selectionController->GetSelectedSet(); SetOverride(active_line, "\\org", ToScriptCoords(feature->pos).PStr());
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::DoRefresh() { void VisualToolRotateXY::DoRefresh() {
if (!curDiag) return; if (!active_line) return;
int posx, posy;
GetLinePosition(curDiag,posx,posy,org->x,org->y); if (!(org->pos = GetLineOrigin(active_line)))
GetLineRotation(curDiag,curAngleX,curAngleY,curAngleZ); org->pos = GetLinePosition(active_line);
org->pos = FromScriptCoords(org->pos);
GetLineRotation(active_line, angle_x, angle_y, angle_z);
} }

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -41,20 +28,20 @@
/// @class VisualToolRotateXY /// @class VisualToolRotateXY
/// @brief DOCME /// @brief DOCME
class VisualToolRotateXY : public VisualTool<VisualDraggableFeature> { class VisualToolRotateXY : public VisualTool<VisualDraggableFeature> {
float curAngleX,startAngleX,origAngleX; float angle_x; /// Current x rotation
float curAngleY,startAngleY,origAngleY; float angle_y; /// Current y rotation
float curAngleZ; 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; Feature *org;
void DoRefresh();
void Draw();
void UpdateDrag(feature_iterator feature);
bool InitializeHold(); bool InitializeHold();
void UpdateHold(); void UpdateHold();
void CommitHold();
void CommitDrag(feature_iterator feature);
void DoRefresh();
void Draw();
public: public:
VisualToolRotateXY(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *); VisualToolRotateXY(VideoDisplay *parent, agi::Context *context);
}; };

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -39,133 +26,110 @@
#include <cmath> #include <cmath>
#endif #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 "visual_tool_rotatez.h"
#include "utils.h"
static const float deg2rad = 3.1415926536f / 180.f; static const float deg2rad = 3.1415926536f / 180.f;
static const float rad2deg = 180.f / 3.1415926536f; static const float rad2deg = 180.f / 3.1415926536f;
VisualToolRotateZ::VisualToolRotateZ(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *) VisualToolRotateZ::VisualToolRotateZ(VideoDisplay *parent, agi::Context *context)
: VisualTool<VisualDraggableFeature>(parent, context, video) : VisualTool<VisualDraggableFeature>(parent, context)
{ {
features.resize(1); features.resize(1);
org = &features.back(); org = &features.back();
org->type = DRAG_BIG_TRIANGLE; org->type = DRAG_BIG_TRIANGLE;
DoRefresh();
} }
void VisualToolRotateZ::Draw() { void VisualToolRotateZ::Draw() {
if (!curDiag) return; if (!active_line) return;
// Draw pivot
DrawAllFeatures(); DrawAllFeatures();
int radius = (int)sqrt(double((posx-org->x)*(posx-org->x)+(posy-org->y)*(posy-org->y))); float radius = (pos - org->pos).Len();
int oRadius = radius; float oRadius = radius;
if (radius < 50) radius = 50; 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);
// Set up the projection // Set up the projection
glMatrixMode(GL_MODELVIEW); gl.SetOrigin(org->pos);
glPushMatrix(); gl.SetRotation(rotation_x, rotation_y, 0);
glLoadIdentity(); gl.SetScale(scale);
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);
// Draw the circle // 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 // Draw markers around circle
int markers = 6; int markers = 6;
float markStart = -90.f / markers; float markStart = -90.f / markers;
float markEnd = markStart+(180.f/markers); float markEnd = markStart + (180.f / markers);
for (int i=0;i<markers;i++) { for (int i = 0; i < markers; ++i) {
float angle = i*(360.f/markers); float angle = i * (360.f / markers);
DrawRing(0,0,radius+30,radius+12,radius/radius,angle+markStart,angle+markEnd); gl.DrawRing(Vector2D(), radius+30, radius+12, 1.0, angle+markStart, angle+markEnd);
} }
// Draw the baseline // Draw the baseline through the origin showing current rotation
SetLineColour(colour[3],1.f,2); Vector2D angle_vec(Vector2D::FromAngle(angle * deg2rad));
DrawLine(deltax,deltay,-deltax,-deltay); gl.SetLineColour(colour[3], 1, 2);
gl.DrawLine(angle_vec * -radius, angle_vec * radius);
// Draw the connection line if (org->pos != pos) {
if (org->x != posx || org->y != posy) { Vector2D rotated_pos = Vector2D::FromAngle(angle * deg2rad - (pos - org->pos).Angle()) * oRadius;
double angle = atan2(double(org->y-posy),double(posx-org->x)) + curAngle*deg2rad;
int fx = int(cos(angle)*oRadius); // Draw the line from origin to rotated position
int fy = -int(sin(angle)*oRadius); gl.DrawLine(Vector2D(), rotated_pos);
DrawLine(0,0,fx,fy);
int mdx = int(cos(curAngle*deg2rad)*20); // Draw the line under the text
int mdy = int(-sin(curAngle*deg2rad)*20); gl.DrawLine(rotated_pos - angle_vec * 20, rotated_pos + angle_vec * 20);
DrawLine(fx-mdx,fy-mdy,fx+mdx,fy+mdy);
} }
// Draw the rotation line // Draw the fake features on the ring
SetLineColour(colour[0],1.f,1); gl.SetLineColour(colour[0], 1.f, 1);
SetFillColour(colour[1],0.3f); gl.SetFillColour(colour[1], 0.3f);
DrawCircle(deltax,deltay,4); gl.DrawCircle(angle_vec * radius, 4);
gl.DrawCircle(angle_vec * -radius, 4);
glPopMatrix(); // Clear the projection
gl.ResetTransform();
// Draw line to mouse // Draw line to mouse if it isn't over the origin feature
if (!dragging && curFeature == features.end() && video.x > INT_MIN && video.y > INT_MIN) { if (mouse_pos && (mouse_pos - org->pos).SquareLen() > 100) {
SetLineColour(colour[0]); gl.SetLineColour(colour[0]);
DrawLine(org->x,org->y,video.x,video.y); gl.DrawLine(org->pos, mouse_pos);
} }
} }
bool VisualToolRotateZ::InitializeHold() { bool VisualToolRotateZ::InitializeHold() {
startAngle = atan2(double(org->y-video.y),double(video.x-org->x)) * rad2deg; orig_angle = angle + (org->pos - mouse_pos).Angle() * rad2deg;
origAngle = curAngle;
curDiag->StripTag("\\frz");
curDiag->StripTag("\\fr");
return true; return true;
} }
void VisualToolRotateZ::UpdateHold() { void VisualToolRotateZ::UpdateHold() {
float screenAngle = atan2(double(org->y-video.y),double(video.x-org->x)) * rad2deg; angle = orig_angle - (org->pos - mouse_pos).Angle() * rad2deg;
curAngle = fmodf(screenAngle - startAngle + origAngle + 360.f, 360.f);
// Oh Snap if (ctrl_down)
if (ctrlDown) { angle = floorf(angle / 30.f + .5f) * 30.f;
curAngle = floorf(curAngle/30.f+.5f)*30.f;
if (curAngle > 359.f) curAngle = 0.f; angle = fmodf(angle + 360.f, 360.f);
}
SetSelectedOverride("\\frz", wxString::Format("(%0.3g)", angle));
} }
void VisualToolRotateZ::CommitHold() { void VisualToolRotateZ::UpdateDrag(feature_iterator feature) {
Selection sel = c->selectionController->GetSelectedSet(); SetOverride(active_line, "\\org", ToScriptCoords(feature->pos).PStr());
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::DoRefresh() { void VisualToolRotateZ::DoRefresh() {
if (!curDiag) return; if (!active_line) return;
GetLinePosition(curDiag, posx, posy, org->x, org->y);
GetLineRotation(curDiag, rx, ry, curAngle); pos = FromScriptCoords(GetLinePosition(active_line));
GetLineScale(curDiag, scaleX, scaleY); 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);
} }

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -37,29 +24,30 @@
#include "visual_feature.h" #include "visual_feature.h"
#include "visual_tool.h" #include "visual_tool.h"
/// DOCME /// DOCME
/// @class VisualToolRotateZ /// @class VisualToolRotateZ
/// @brief DOCME /// @brief DOCME
/// ///
/// DOCME /// DOCME
class VisualToolRotateZ : public VisualTool<VisualDraggableFeature> { class VisualToolRotateZ : public VisualTool<VisualDraggableFeature> {
float curAngle, startAngle, origAngle; float angle; ///< Current Z rotation
Feature *org; float orig_angle; ///< Z rotation at the beginning of the current hold
int posx, posy; Vector2D pos; ///< Position of the dialogue line
float rx, ry; Vector2D scale; ///< Current scale
float scaleX, scaleY;
float rotation_x; ///< Current X rotation
float rotation_y; ///< Current Y rotation
Feature *org; ///< The origin feature
bool InitializeHold(); bool InitializeHold();
void UpdateHold(); void UpdateHold();
void CommitHold();
void CommitDrag(feature_iterator feature); void UpdateDrag(feature_iterator feature);
void DoRefresh(); void DoRefresh();
void Draw(); void Draw();
bool Update() { return true; }
public: public:
VisualToolRotateZ(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *); VisualToolRotateZ(VideoDisplay *parent, agi::Context *context);
}; };

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -36,126 +23,95 @@
#include "config.h" #include "config.h"
#ifndef AGI_PRE #ifndef AGI_PRE
#include <math.h> #include <cmath>
#endif #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" #include "visual_tool_scale.h"
VisualToolScale::VisualToolScale(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *) #include "utils.h"
: VisualTool<VisualDraggableFeature>(parent, context, video)
, curScaleX(0.f) VisualToolScale::VisualToolScale(VideoDisplay *parent, agi::Context *context)
, origScaleX(0.f) : VisualTool<VisualDraggableFeature>(parent, context)
, curScaleY(0.f)
, origScaleY(0.f)
, startX(0)
, startY(0)
{ {
DoRefresh();
} }
void VisualToolScale::Draw() { void VisualToolScale::Draw() {
if (!curDiag) return; if (!active_line) return;
int len = 160; // The length in pixels of the 100% zoom
int dx = mid(len/2+10,posx,video.w-len/2-30); static const int base_len = 160;
int dy = mid(len/2+10,posy,video.h-len/2-30); // The width of the y scale guide/height of the x scale guide
static const int guide_size = 10;
SetLineColour(colour[0]); // Ensure that the scaling UI is comfortably visible on screen
SetFillColour(colour[1],0.3f); 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 // Set the origin to the base point and apply the line's rotation
glMatrixMode(GL_MODELVIEW); gl.SetOrigin(base_point);
glPushMatrix(); gl.SetRotation(rx, ry, rz);
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;
// Draw length markers Vector2D scale_half_length = scale * base_len / 200;
SetLineColour(colour[3],1.f,2); float minor_dim_offset = base_len / 2 + guide_size * 1.5f;
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);
// Draw horizontal scale // The ends of the current scale amount lines
SetLineColour(colour[0],1.f,1); Vector2D x_p1(minor_dim_offset, -scale_half_length.Y());
DrawRectangle(-len/2,drawY,len/2+1,drawY+5); Vector2D x_p2(minor_dim_offset, scale_half_length.Y());
SetLineColour(colour[0],1.f,2); Vector2D y_p1(-scale_half_length.X(), minor_dim_offset);
DrawLine(-len/2+1,drawY+5,-len/2+1,drawY+15); Vector2D y_p2(scale_half_length.X(), minor_dim_offset);
DrawLine(len/2,drawY+5,len/2,drawY+15);
// Draw vertical scale // Current scale amount lines
SetLineColour(colour[0],1.f,1); gl.SetLineColour(colour[3], 1.f, 2);
DrawRectangle(drawX,-len/2,drawX+5,len/2+1); gl.DrawLine(x_p1, x_p2);
SetLineColour(colour[0],1.f,2); gl.DrawLine(y_p1, y_p2);
DrawLine(drawX+5,-len/2+1,drawX+15,-len/2+1);
DrawLine(drawX+5,len/2,drawX+15,len/2);
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() { bool VisualToolScale::InitializeHold() {
startX = video.x; initial_scale = scale;
startY = video.y;
origScaleX = curScaleX;
origScaleY = curScaleY;
curDiag->StripTag("\\fscx");
curDiag->StripTag("\\fscy");
return true; return true;
} }
void VisualToolScale::UpdateHold() { void VisualToolScale::UpdateHold() {
// Deltas Vector2D delta = mouse_pos - drag_start;
int deltaX = video.x - startX; if (shift_down)
int deltaY = startY - video.y; delta = delta.SingleAxis();
if (shiftDown) {
if (abs(deltaX) >= abs(deltaY)) deltaY = 0;
else deltaX = 0;
}
// Calculate scale = Vector2D().Max(delta * 1.25f + initial_scale);
curScaleX = std::max(deltaX*1.25f + origScaleX, 0.f); if (ctrl_down)
curScaleY = std::max(deltaY*1.25f + origScaleY, 0.f); scale = scale.Round(25.f);
// Oh Snap SetSelectedOverride("\\fscx", wxString::Format("(%0.3g)", scale.X()));
if (ctrlDown) { SetSelectedOverride("\\fscy", wxString::Format("(%0.3g)", scale.Y()));
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));
}
} }
void VisualToolScale::DoRefresh() { void VisualToolScale::DoRefresh() {
if (!curDiag) return; if (!active_line) return;
GetLineScale(curDiag, curScaleX, curScaleY); GetLineScale(active_line, scale);
GetLinePosition(curDiag, posx, posy); GetLineRotation(active_line, rx, ry, rz);
GetLineRotation(curDiag, rx, ry, rz); pos = FromScriptCoords(GetLinePosition(active_line));
} }

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -41,20 +28,19 @@
/// @class VisualToolScale /// @class VisualToolScale
/// @brief DOCME /// @brief DOCME
class VisualToolScale : public VisualTool<VisualDraggableFeature> { class VisualToolScale : public VisualTool<VisualDraggableFeature> {
float curScaleX, origScaleX; Vector2D scale; ///< The current scale
float curScaleY, origScaleY; Vector2D initial_scale; ///< The scale at the beginning of the current hold
Vector2D pos; ///< Position of the line
int startX, startY;
int posx, posy;
float rx, ry, rz;
float rx; ///< X rotation
float ry; ///< Y rotation
float rz; ///< Z rotation
bool InitializeHold(); bool InitializeHold();
void UpdateHold(); void UpdateHold();
void CommitHold();
void DoRefresh(); void DoRefresh();
void Draw(); void Draw();
public: public:
VisualToolScale(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar *); VisualToolScale(VideoDisplay *parent, agi::Context *context);
}; };

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -38,30 +25,18 @@
#ifndef AGI_PRE #ifndef AGI_PRE
#include <wx/toolbar.h> #include <wx/toolbar.h>
#ifdef HAVE_APPLE_OPENGL_FRAMEWORK
#include <OpenGL/glext.h>
#else
#include "gl/glext.h"
#endif
#include <algorithm> #include <algorithm>
#endif #endif
#ifdef __APPLE__
/// Noop macro, this should never be defined in a header.
#define APIENTRY
#endif
#include "config.h" #include "config.h"
#include "ass_dialogue.h" #include "ass_dialogue.h"
#include "libresrc/libresrc.h" #include "libresrc/libresrc.h"
#include "utils.h" #include "utils.h"
#include "video_display.h"
/// Button IDs /// Button IDs
enum { enum {
BUTTON_DRAG = VISUAL_SUB_TOOL_START, BUTTON_DRAG = 1300,
BUTTON_LINE, BUTTON_LINE,
BUTTON_BICUBIC, BUTTON_BICUBIC,
BUTTON_CONVERT, BUTTON_CONVERT,
@ -72,34 +47,28 @@ enum {
BUTTON_LAST // Leave this at the end and don't use it BUTTON_LAST // Leave this at the end and don't use it
}; };
template<class C, class O, class M> VisualToolVectorClip::VisualToolVectorClip(VideoDisplay *parent, agi::Context *context)
static void for_each_iter(C &container, O obj, M method) { : VisualTool<VisualToolVectorClipDraggableFeature>(parent, context)
typename C::iterator end = container.end(); , spline(*this)
for (typename C::iterator cur = container.begin(); cur != end; ++cur) { {
(obj ->* method)(cur);
}
} }
VisualToolVectorClip::VisualToolVectorClip(VideoDisplay *parent, agi::Context *context, VideoState const& video, wxToolBar * toolBar) void VisualToolVectorClip::SetToolbar(wxToolBar *toolBar) {
: VisualTool<VisualToolVectorClipDraggableFeature>(parent, context, video) this->toolBar = toolBar;
, spline(*parent) toolBar->AddTool(BUTTON_DRAG, _("Drag"), GETIMAGE(visual_vector_clip_drag_24), _("Drag control points."), wxITEM_CHECK);
, toolBar(toolBar) 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->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->AddSeparator();
toolBar->AddTool(BUTTON_CONVERT,_("Convert"),GETIMAGE(visual_vector_clip_convert_24),_("Converts a segment between line and bicubic."),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_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_REMOVE, _("Remove"), GETIMAGE(visual_vector_clip_remove_24), _("Removes a control point."), wxITEM_CHECK);
toolBar->AddSeparator(); toolBar->AddSeparator();
toolBar->AddTool(BUTTON_FREEHAND,_("Freehand"),GETIMAGE(visual_vector_clip_freehand_24),_("Draws a freehand shape."),wxITEM_CHECK); 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->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->ToggleTool(BUTTON_DRAG, true);
toolBar->Realize(); toolBar->Realize();
toolBar->Show(true); toolBar->Show(true);
toolBar->Bind(wxEVT_COMMAND_TOOL_CLICKED, &VisualToolVectorClip::OnSubTool, this);
DoRefresh();
SetMode(features.empty()); SetMode(features.empty());
} }
@ -107,35 +76,23 @@ void VisualToolVectorClip::OnSubTool(wxCommandEvent &event) {
SetMode(event.GetId() - BUTTON_DRAG); 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 // Manually enforce radio behavior as we want one selection in the bar
// rather than one per group // rather than one per group
for (int i=BUTTON_DRAG;i<BUTTON_LAST;i++) { for (int i = BUTTON_DRAG; i < BUTTON_LAST; i++)
toolBar->ToggleTool(i,i == newMode + BUTTON_DRAG); toolBar->ToggleTool(i, i == new_mode + BUTTON_DRAG);
}
mode = newMode;
}
// Substitute for glMultiDrawArrays for sub-1.4 OpenGL mode = new_mode;
// 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
static bool is_move(SplineCurve const& c) { static bool is_move(SplineCurve const& c) {
return c.type == CURVE_POINT; return c.type == SplineCurve::POINT;
} }
void VisualToolVectorClip::Draw() { void VisualToolVectorClip::Draw() {
if (!curDiag) return; if (!active_line) return;
if (spline.empty()) return; if (spline.empty()) return;
GL_EXT(PFNGLMULTIDRAWARRAYSPROC, glMultiDrawArrays);
// Parse vector // Parse vector
std::vector<float> points; std::vector<float> points;
std::vector<int> start; std::vector<int> start;
@ -144,78 +101,33 @@ void VisualToolVectorClip::Draw() {
spline.GetPointList(points, start, count); spline.GetPointList(points, start, count);
assert(!start.empty()); assert(!start.empty());
assert(!count.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 gl.DrawMultiPolygon(points, start, count, video_res, !inverse);
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());
Vector2D pt; Vector2D pt;
float t; float t;
Spline::iterator highCurve; Spline::iterator highlighted_curve;
spline.GetClosestParametricPoint(Vector2D(video.x, video.y), highCurve, t, pt); spline.GetClosestParametricPoint(mouse_pos, highlighted_curve, t, pt);
// Draw highlighted line // Draw highlighted line
if ((mode == 3 || mode == 4) && curFeature == features.end() && points.size() > 2) { if ((mode == 3 || mode == 4) && active_feature == features.end() && points.size() > 2) {
std::vector<float> highPoints; std::vector<float> highlighted_points;
spline.GetPointList(highPoints, highCurve); spline.GetPointList(highlighted_points, highlighted_curve);
if (!highPoints.empty()) { if (!highlighted_points.empty()) {
glVertexPointer(2, GL_FLOAT, 0, &highPoints[0]); gl.SetLineColour(colour[2], 1.f, 2);
SetLineColour(colour[2], 1.f, 2); gl.DrawLineStrip(2, highlighted_points);
SetModeLine();
glDrawArrays(GL_LINE_STRIP, 0, highPoints.size() / 2);
} }
} }
glDisableClientState(GL_VERTEX_ARRAY);
// Draw lines connecting the bicubic features // Draw lines connecting the bicubic features
SetLineColour(colour[3],0.9f,1); gl.SetLineColour(colour[3], 0.9f, 1);
for (Spline::iterator cur=spline.begin();cur!=spline.end();cur++) { for (Spline::iterator cur = spline.begin(); cur != spline.end(); ++cur) {
if (cur->type == CURVE_BICUBIC) { if (cur->type == SplineCurve::BICUBIC) {
DrawDashedLine(cur->p1.x,cur->p1.y,cur->p2.x,cur->p2.y,6); gl.DrawDashedLine(cur->p1, cur->p2, 6);
DrawDashedLine(cur->p3.x,cur->p3.y,cur->p4.x,cur->p4.y,6); gl.DrawDashedLine(cur->p3, cur->p4, 6);
} }
} }
@ -223,94 +135,84 @@ void VisualToolVectorClip::Draw() {
// Draw preview of inserted line // Draw preview of inserted line
if (mode == 1 || mode == 2) { 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); Spline::reverse_iterator c0 = std::find_if(spline.rbegin(), spline.rend(), is_move);
SplineCurve *c1 = &spline.back(); SplineCurve *c1 = &spline.back();
DrawDashedLine(video.x,video.y,c0->p1.x,c0->p1.y,6); gl.DrawDashedLine(mouse_pos, c0->p1, 6);
DrawDashedLine(video.x,video.y,c1->EndPoint().x,c1->EndPoint().y,6); gl.DrawDashedLine(mouse_pos, c1->EndPoint(), 6);
} }
} }
// Draw preview of insert point // 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) { void VisualToolVectorClip::MakeFeature(Spline::iterator cur) {
Feature feat; Feature feat;
if (cur->type == CURVE_POINT) { feat.curve = cur;
feat.x = (int)cur->p1.x;
feat.y = (int)cur->p1.y; if (cur->type == SplineCurve::POINT) {
feat.pos = cur->p1;
feat.type = DRAG_SMALL_CIRCLE; feat.type = DRAG_SMALL_CIRCLE;
feat.curve = cur;
feat.point = 0; feat.point = 0;
features.push_back(feat);
} }
else if (cur->type == SplineCurve::LINE) {
else if (cur->type == CURVE_LINE) { feat.pos = cur->p2;
feat.x = (int)cur->p2.x;
feat.y = (int)cur->p2.y;
feat.type = DRAG_SMALL_CIRCLE; feat.type = DRAG_SMALL_CIRCLE;
feat.curve = cur;
feat.point = 1; feat.point = 1;
features.push_back(feat);
} }
else if (cur->type == SplineCurve::BICUBIC) {
else if (cur->type == CURVE_BICUBIC) {
// Control points // Control points
feat.x = (int)cur->p2.x; feat.pos = cur->p2;
feat.y = (int)cur->p2.y;
feat.curve = cur;
feat.point = 1; feat.point = 1;
feat.type = DRAG_SMALL_SQUARE; feat.type = DRAG_SMALL_SQUARE;
features.push_back(feat); features.push_back(feat);
feat.x = (int)cur->p3.x; feat.pos = cur->p3;
feat.y = (int)cur->p3.y;
feat.point = 2; feat.point = 2;
features.push_back(feat); features.push_back(feat);
// End point // End point
feat.x = (int)cur->p4.x; feat.pos = cur->p4;
feat.y = (int)cur->p4.y;
feat.type = DRAG_SMALL_CIRCLE; feat.type = DRAG_SMALL_CIRCLE;
feat.point = 3; feat.point = 3;
features.push_back(feat);
} }
features.push_back(feat);
} }
void VisualToolVectorClip::MakeFeatures() { void VisualToolVectorClip::MakeFeatures() {
ClearSelection(); sel_features.clear();
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() { void VisualToolVectorClip::Save() {
SetOverride(curDiag, inverse ? "\\iclip" : "\\clip", "(" + spline.EncodeToASS() + ")"); SetOverride(active_line, inverse ? "\\iclip" : "\\clip", "(" + spline.EncodeToASS() + ")");
} }
void VisualToolVectorClip::UpdateDrag(feature_iterator feature) { void VisualToolVectorClip::UpdateDrag(feature_iterator feature) {
spline.MovePoint(feature->curve,feature->point,Vector2D(feature->x,feature->y)); spline.MovePoint(feature->curve, feature->point, feature->pos);
}
void VisualToolVectorClip::CommitDrag(feature_iterator) {
Save(); Save();
} }
bool VisualToolVectorClip::InitializeDrag(feature_iterator feature) { bool VisualToolVectorClip::InitializeDrag(feature_iterator feature) {
if (mode != 5) return true; 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 // Deleting bicubic curve handles, so convert to line
feature->curve->type = CURVE_LINE; feature->curve->type = SplineCurve::LINE;
feature->curve->p2 = feature->curve->p4; feature->curve->p2 = feature->curve->p4;
} }
else { else {
Spline::iterator next = feature->curve; Spline::iterator next = feature->curve;
next++; next++;
if (next != spline.end()) { if (next != spline.end()) {
if (feature->curve->type == CURVE_POINT) { if (feature->curve->type == SplineCurve::POINT) {
next->p1 = next->EndPoint(); next->p1 = next->EndPoint();
next->type = CURVE_POINT; next->type = SplineCurve::POINT;
} }
else { else {
next->p1 = feature->curve->p1; next->p1 = feature->curve->p1;
@ -319,7 +221,7 @@ bool VisualToolVectorClip::InitializeDrag(feature_iterator feature) {
spline.erase(feature->curve); spline.erase(feature->curve);
} }
curFeature = features.end(); active_feature = features.end();
Save(); Save();
MakeFeatures(); MakeFeatures();
@ -333,21 +235,20 @@ bool VisualToolVectorClip::InitializeHold() {
if (mode == 1 || mode == 2) { if (mode == 1 || mode == 2) {
SplineCurve curve; SplineCurve curve;
// Set start position // New spline beginning at the clicked point
if (!spline.empty()) { if (spline.empty()) {
curve.p1 = spline.back().EndPoint(); curve.p1 = mouse_pos;
curve.type = mode == 1 ? CURVE_LINE : CURVE_BICUBIC; curve.type = SplineCurve::POINT;
} }
// First point
else { else {
curve.p1 = Vector2D(video.x,video.y); // Continue from the spline in progress
curve.type = CURVE_POINT; // 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); spline.push_back(curve);
ClearSelection(); sel_features.clear();
MakeFeature(--spline.end()); MakeFeature(--spline.end());
UpdateHold(); UpdateHold();
return true; return true;
@ -359,93 +260,85 @@ bool VisualToolVectorClip::InitializeHold() {
Vector2D pt; Vector2D pt;
Spline::iterator curve; Spline::iterator curve;
float t; 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 (mode == 3) {
if (curve != spline.end()) { if (curve != spline.end()) {
if (curve->type == CURVE_LINE) { if (curve->type == SplineCurve::LINE) {
curve->type = CURVE_BICUBIC; curve->type = SplineCurve::BICUBIC;
curve->p4 = curve->p2; curve->p4 = curve->p2;
curve->p2 = curve->p1 * 0.75 + curve->p4 * 0.25; curve->p2 = curve->p1 * 0.75 + curve->p4 * 0.25;
curve->p3 = curve->p1 * 0.25 + curve->p4 * 0.75; curve->p3 = curve->p1 * 0.25 + curve->p4 * 0.75;
} }
else if (curve->type == CURVE_BICUBIC) { else if (curve->type == SplineCurve::BICUBIC) {
curve->type = CURVE_LINE; curve->type = SplineCurve::LINE;
curve->p2 = curve->p4; curve->p2 = curve->p4;
} }
} }
} }
// Insert // Insert
else { else {
// Check if there is at least one curve to split
if (spline.empty()) return false; if (spline.empty()) return false;
// Split the curve // Split the curve
if (curve == spline.end()) { if (curve == spline.end()) {
SplineCurve ct; SplineCurve ct(spline.back().EndPoint(), spline.front().p1);
ct.type = CURVE_LINE; ct.p2 = ct.p1 * (1 - t) + ct.p2 * t;
ct.p1 = spline.back().EndPoint();
ct.p2 = spline.front().p1;
ct.p2 = ct.p1*(1-t) + ct.p2*t;
spline.push_back(ct); spline.push_back(ct);
} }
else { else {
SplineCurve c2; SplineCurve c2;
curve->Split(*curve,c2,t); curve->Split(*curve, c2, t);
spline.insert(++curve, c2); spline.insert(++curve, c2);
} }
} }
// Commit
Save(); Save();
MakeFeatures(); MakeFeatures();
Commit(); Commit();
return false; return false;
} }
// Freehand // Freehand spline draw
if (mode == 6 || mode == 7) { if (mode == 6 || mode == 7) {
ClearSelection(); sel_features.clear();
features.clear(); features.clear();
active_feature = features.end();
spline.clear(); spline.clear();
SplineCurve curve; spline.push_back(SplineCurve(mouse_pos));
curve.type = CURVE_POINT;
curve.p1.x = video.x;
curve.p1.y = video.y;
spline.push_back(curve);
return true; return true;
} }
/// @todo box selection?
if (mode == 0) {
return false;
}
// Nothing to do for mode 5 (remove)
return false; return false;
} }
void VisualToolVectorClip::UpdateHold() { void VisualToolVectorClip::UpdateHold() {
// Insert line
if (mode == 1) { if (mode == 1) {
spline.back().EndPoint() = Vector2D(video.x,video.y); spline.back().EndPoint() = mouse_pos;
features.back().x = video.x; features.back().pos = mouse_pos;
features.back().y = video.y;
} }
// Insert bicubic // Insert bicubic
else if (mode == 2) { else if (mode == 2) {
SplineCurve &curve = spline.back(); SplineCurve &curve = spline.back();
curve.EndPoint() = Vector2D(video.x,video.y); curve.EndPoint() = mouse_pos;
// Control points // Control points
if (spline.size() > 1) { if (spline.size() > 1) {
std::list<SplineCurve>::reverse_iterator iter = spline.rbegin(); SplineCurve &c0 = spline.back();
iter++; float len = (curve.p4 - curve.p1).Len();
SplineCurve &c0 = *iter; curve.p2 = (c0.type == SplineCurve::LINE ? c0.p2 - c0.p1 : c0.p4 - c0.p3).Unit() * (0.25f * len) + curve.p1;
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;
} }
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; curve.p3 = curve.p1 * 0.25 + curve.p4 * 0.75;
MakeFeatures(); MakeFeatures();
} }
@ -454,27 +347,19 @@ void VisualToolVectorClip::UpdateHold() {
else if (mode == 6 || mode == 7) { else if (mode == 6 || mode == 7) {
// See if distance is enough // See if distance is enough
Vector2D const& last = spline.back().EndPoint(); Vector2D const& last = spline.back().EndPoint();
int len = (int)Vector2D(last.x-video.x, last.y-video.y).Len(); float len = (last - mouse_pos).SquareLen();
if (mode == 6 && len < 30) return; if (mode == 6 && len < 900) return;
if (mode == 7 && len < 60) return; if (mode == 7 && len < 3600) return;
// Generate curve and add it spline.push_back(SplineCurve(last, mouse_pos));
SplineCurve curve;
curve.type = CURVE_LINE;
curve.p1 = Vector2D(last.x,last.y);
curve.p2 = Vector2D(video.x,video.y);
spline.push_back(curve);
MakeFeature(--spline.end()); MakeFeature(--spline.end());
} }
}
void VisualToolVectorClip::CommitHold() {
if (mode == 3 || mode == 4) return; if (mode == 3 || mode == 4) return;
// Smooth spline // Smooth spline
if (!holding && mode == 7) { if (!holding && mode == 7)
spline.Smooth(); spline.Smooth();
}
Save(); Save();
@ -486,11 +371,11 @@ void VisualToolVectorClip::CommitHold() {
} }
void VisualToolVectorClip::DoRefresh() { void VisualToolVectorClip::DoRefresh() {
if (!curDiag) return; if (!active_line) return;
wxString vect; wxString vect;
int scale; int scale;
vect = GetLineVectorClip(curDiag,scale,inverse); vect = GetLineVectorClip(active_line, scale, inverse);
spline.DecodeFromASS(vect); spline.DecodeFromASS(vect);
MakeFeatures(); MakeFeatures();
@ -498,6 +383,7 @@ void VisualToolVectorClip::DoRefresh() {
} }
void VisualToolVectorClip::SelectAll() { void VisualToolVectorClip::SelectAll() {
ClearSelection(); sel_features.clear();
for_each_iter(features, this, &VisualToolVectorClip::AddSelection); for (feature_iterator it = features.begin(); it != features.end(); ++it)
sel_features.insert(it);
} }

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro // Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
// //
// Redistribution and use in source and binary forms, with or without // Permission to use, copy, modify, and distribute this software for any
// modification, are permitted provided that the following conditions are met: // 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, // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// this list of conditions and the following disclaimer. // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// * Redistributions in binary form must reproduce the above copyright notice, // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// this list of conditions and the following disclaimer in the documentation // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// and/or other materials provided with the distribution. // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// * Neither the name of the Aegisub Group nor the names of its contributors // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// may be used to endorse or promote products derived from this software // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// //
// Aegisub Project http://www.aegisub.org/ // Aegisub Project http://www.aegisub.org/
// //
@ -43,8 +30,7 @@ class wxToolBar;
/// @class VisualToolVectorClipDraggableFeature /// @class VisualToolVectorClipDraggableFeature
/// @brief VisualDraggableFeature with information about a feature's location /// @brief VisualDraggableFeature with information about a feature's location
/// in the spline /// in the spline
class VisualToolVectorClipDraggableFeature : public VisualDraggableFeature { struct VisualToolVectorClipDraggableFeature : public VisualDraggableFeature {
public:
/// Which curve in the spline this feature is a point on /// Which curve in the spline this feature is a point on
Spline::iterator curve; Spline::iterator curve;
/// 0-3; indicates which part of the curve this point is /// 0-3; indicates which part of the curve this point is
@ -77,17 +63,15 @@ class VisualToolVectorClip : public VisualTool<VisualToolVectorClipDraggableFeat
bool InitializeHold(); bool InitializeHold();
void UpdateHold(); void UpdateHold();
void CommitHold();
void UpdateDrag(feature_iterator feature); void UpdateDrag(feature_iterator feature);
void CommitDrag(feature_iterator feature);
bool InitializeDrag(feature_iterator feature); bool InitializeDrag(feature_iterator feature);
void DoRefresh(); void DoRefresh();
void Draw(); void Draw();
bool Update() { return mode >= 1 && mode <= 4; }
public: 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 /// Subtoolbar button click handler
void OnSubTool(wxCommandEvent &event); void OnSubTool(wxCommandEvent &event);