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"
>
</File>
<File
RelativePath="..\..\src\command\vis_tool.cpp"
>
</File>
</Filter>
<Filter
Name="Preferences"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -198,6 +198,13 @@ INSERT_ICON("video/opt/autoscroll", toggle_video_autoscroll)
INSERT_ICON("video/play", button_play)
INSERT_ICON("video/play/line", button_playline)
INSERT_ICON("video/stop", button_pause)
INSERT_ICON("video/tool/clip", visual_clip)
INSERT_ICON("video/tool/cross", visual_standard)
INSERT_ICON("video/tool/drag", visual_move)
INSERT_ICON("video/tool/rotate/xy", visual_rotatexy)
INSERT_ICON("video/tool/rotate/z", visual_rotatez)
INSERT_ICON("video/tool/scale", visual_scale)
INSERT_ICON("video/tool/vector_clip", visual_vector_clip)
INSERT_ICON("video/zoom/in", zoom_in_button)
INSERT_ICON("video/zoom/out", zoom_out_button)

View file

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

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 "validators.h"
#include "video_context.h"
#include "video_display.h"
#include "video_provider_manager.h"
DialogProperties::DialogProperties(agi::Context *c)

View file

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

View file

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

View file

@ -342,20 +342,30 @@ void OpenGLTextTexture::Insert(OpenGLTextGlyph &glyph) {
/// Draw a glyph at (x,y)
void OpenGLTextGlyph::Draw(int x,int y) const {
// Store matrix and translate
glPushMatrix();
glTranslatef((float)x,(float)y,0.0f);
glBindTexture(GL_TEXTURE_2D, tex);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glBegin(GL_QUADS);
glTexCoord2f(x1,y1); glVertex2f(0,0); // Top-left
glTexCoord2f(x1,y2); glVertex2f(0,h); // Bottom-left
glTexCoord2f(x2,y2); glVertex2f(w,h); // Bottom-right
glTexCoord2f(x2,y1); glVertex2f(w,0); // Top-right
glEnd();
float tex_coords[] = {
x1, y1,
x1, y2,
x2, y2,
x2, y1
};
glPopMatrix();
float vert_coords[] = {
x, y,
x, y + h,
x + w, y + h,
x + w, y
};
glVertexPointer(2, GL_FLOAT, 0, vert_coords);
glTexCoordPointer(2, GL_FLOAT, 0, tex_coords);
glDrawArrays(GL_QUADS, 0, 4);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
/// @brief DOCME

View file

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

View file

@ -34,5 +34,5 @@ namespace toolbar {
/// @param context Project context
/// @param hotkey Hotkey context for the tooltip
void AttachToolbar(wxFrame *frame, std::string const& name, agi::Context *context, std::string const& hotkey);
wxToolBar *GetToolbar(wxWindow *parent, std::string const& name, agi::Context *context, std::string const& hotkey);
wxToolBar *GetToolbar(wxWindow *parent, std::string const& name, agi::Context *context, std::string const& hotkey, bool vertical = false);
}

View file

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

View file

@ -61,5 +61,16 @@
"",
"app/options",
"grid/tag/cycle_hiding"
],
"visual_tools" : [
"video/tool/cross",
"video/tool/drag",
"video/tool/rotate/z",
"video/tool/rotate/xy",
"video/tool/scale",
"video/tool/clip",
"video/tool/vector_clip",
"",
"help/video"
]
}

View file

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

View file

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

View file

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

View file

@ -34,57 +34,54 @@
/// @ingroup visual_ts
///
///////////
// Headers
#include "vector2d.h"
/// DOCME
enum CurveType {
/// DOCME
CURVE_INVALID,
/// DOCME
CURVE_POINT,
/// DOCME
CURVE_LINE,
/// DOCME
CURVE_BICUBIC
};
/// DOCME
/// @class SplineCurve
/// @brief DOCME
///
/// DOCME
class SplineCurve {
private:
float GetClosestSegmentPart(Vector2D const& p1,Vector2D const& p2,Vector2D const& p3) const;
float GetClosestSegmentDistance(Vector2D const& p1,Vector2D const& p2,Vector2D const& p3) const;
/// Closest t in segment p1-p2 to point p3
float GetClosestSegmentPart(Vector2D p1, Vector2D p2, Vector2D p3) const;
/// Closest distance between p3 and segment p1-p2
float GetClosestSegmentDistance(Vector2D p1, Vector2D p2, Vector2D p3) const;
public:
enum CurveType {
POINT,
LINE,
BICUBIC
};
/// DOCME
/// DOCME
/// DOCME
/// DOCME
Vector2D p1,p2,p3,p4;
Vector2D p1;
Vector2D p2;
Vector2D p3;
Vector2D p4;
/// DOCME
CurveType type;
SplineCurve();
void Split(SplineCurve &c1,SplineCurve &c2,float t=0.5);
void Smooth(Vector2D const& prev,Vector2D const& next,float smooth=1.0f);
SplineCurve(Vector2D p1 = Vector2D());
SplineCurve(Vector2D p1, Vector2D p2);
SplineCurve(Vector2D p1, Vector2D p2, Vector2D p3, Vector2D p4);
/// @brief Split a curve in two using the de Casteljau algorithm
/// @param[out] c1 Curve before split point
/// @param[out] c2 Curve after split point
/// @param t Split point
void Split(SplineCurve &c1, SplineCurve &c2, float t = 0.5f);
/// @brief Smooths the curve
/// @note Based on http://antigrain.com/research/bezier_interpolation/index.html
void Smooth(Vector2D prev, Vector2D next, float smooth = 1.0f);
Vector2D GetPoint(float t) const;
Vector2D& EndPoint();
Vector2D GetClosestPoint(Vector2D const& ref) const;
float GetClosestParam(Vector2D const& ref) const;
float GetQuickDistance(Vector2D const& ref) const;
/// Get point on the curve closest to reference
Vector2D GetClosestPoint(Vector2D ref) const;
/// Get t value for the closest point to reference
float GetClosestParam(Vector2D ref) const;
/// Get distance from ref to the closest point on the curve
float GetQuickDistance(Vector2D ref) const;
};

View file

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

View file

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

View file

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

View file

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

View file

@ -44,6 +44,7 @@
#endif
#include "include/aegisub/context.h"
#include "include/aegisub/toolbar.h"
#include "ass_dialogue.h"
#include "ass_file.h"
@ -104,22 +105,11 @@ VideoBox::VideoBox(wxWindow *parent, bool isDetached, agi::Context *context)
zoomBox = new wxComboBox(this, -1, "75%", wxDefaultPosition, wxDefaultSize, choices, wxCB_DROPDOWN);
// Typesetting buttons
visualToolBar = new wxToolBar(this,-1,wxDefaultPosition,wxDefaultSize,wxTB_VERTICAL|wxTB_FLAT|wxTB_NODIVIDER);
visualToolBar->AddTool(Video_Mode_Standard,_("Standard"),GETIMAGE(visual_standard_24),_("Standard mode, double click sets position."),wxITEM_RADIO);
visualToolBar->AddTool(Video_Mode_Drag,_("Drag"),GETIMAGE(visual_move_24),_("Drag subtitles."),wxITEM_RADIO);
visualToolBar->AddTool(Video_Mode_Rotate_Z,_("Rotate Z"),GETIMAGE(visual_rotatez_24),_("Rotate subtitles on their Z axis."),wxITEM_RADIO);
visualToolBar->AddTool(Video_Mode_Rotate_XY,_("Rotate XY"),GETIMAGE(visual_rotatexy_24),_("Rotate subtitles on their X and Y axes."),wxITEM_RADIO);
visualToolBar->AddTool(Video_Mode_Scale,_("Scale"),GETIMAGE(visual_scale_24),_("Scale subtitles on X and Y axes."),wxITEM_RADIO);
visualToolBar->AddTool(Video_Mode_Clip,_("Clip"),GETIMAGE(visual_clip_24),_("Clip subtitles to a rectangle."),wxITEM_RADIO);
visualToolBar->AddTool(Video_Mode_Vector_Clip,_("Vector Clip"),GETIMAGE(visual_vector_clip_24),_("Clip subtitles to a vectorial area."),wxITEM_RADIO);
visualToolBar->AddSeparator();
visualToolBar->AddTool(wxID_HELP,_("Help"),cmd::get("help/video")->Icon(24),_("Open the manual page for Visual Typesetting."));
visualToolBar->Realize();
visualToolBar = toolbar::GetToolbar(this, "visual_tools", context, "Video", true);
// Avoid ugly themed background on Vista and possibly also Win7
visualToolBar->SetBackgroundStyle(wxBG_STYLE_COLOUR);
visualToolBar->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
// Display
videoDisplay = new VideoDisplay(this,isDetached,zoomBox,this,context);
// Top sizer

View file

@ -92,14 +92,3 @@ public:
VideoBox(wxWindow *parent, bool isDetached, agi::Context *context);
~VideoBox();
};
// IDs
enum {
Video_Mode_Standard = 5000,
Video_Mode_Drag,
Video_Mode_Rotate_Z,
Video_Mode_Rotate_XY,
Video_Mode_Scale,
Video_Mode_Clip,
Video_Mode_Vector_Clip,
};

View file

@ -266,7 +266,8 @@ void VideoContext::JumpToFrame(int n) {
frame_n = mid(0, n, GetLength() - 1);
Seek(n);
GetFrameAsync(frame_n);
Seek(frame_n);
}
void VideoContext::JumpToTime(int ms, agi::vfr::Time end) {
@ -274,11 +275,11 @@ void VideoContext::JumpToTime(int ms, agi::vfr::Time end) {
}
void VideoContext::GetFrameAsync(int n) {
provider->RequestFrame(n,videoFPS.TimeAtFrame(n)/1000.0);
provider->RequestFrame(n, videoFPS.TimeAtFrame(n) / 1000.0);
}
std::tr1::shared_ptr<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 {
@ -326,10 +327,6 @@ void VideoContext::SaveSnapshot(bool raw) {
GetFrame(frame_n,raw)->GetImage().SaveFile(path,wxBITMAP_TYPE_PNG);
}
void VideoContext::GetScriptSize(int &sw,int &sh) {
context->ass->GetResolution(sw,sh);
}
void VideoContext::NextFrame() {
if (!videoProvider.get() || isPlaying || frame_n == videoProvider->GetFrameCount())
return;

View file

@ -232,13 +232,6 @@ public:
/// @param end Type of time
void JumpToTime(int ms, agi::vfr::Time end = agi::vfr::START);
/// @brief Get the height and width of the current script
/// @param[out] w Width
/// @param[out] h Height
///
/// This probably shouldn't be in VideoContext
void GetScriptSize(int &w,int &h);
/// Starting playing the video
void Play();
/// Play the next frame then stop

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,29 +1,16 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro
// All rights reserved.
// Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
//
@ -36,6 +23,7 @@
#pragma once
#ifndef AGI_PRE
#include <deque>
#include <map>
#include <set>
#include <vector>
@ -45,83 +33,158 @@
#include <wx/button.h>
#endif
#include "base_grid.h"
#include <libaegisub/signal.h>
#include "gl_wrap.h"
#include "selection_controller.h"
#include "vector2d.h"
class AssDialogue;
class SubtitlesGrid;
class VideoDisplay;
struct VideoState;
class wxToolBar;
namespace agi {
struct Context;
class OptionValue;
}
/// First window id for visualsubtoolbar items
#define VISUAL_SUB_TOOL_START 1300
/// @class VisualToolBase
/// @brief Base class for visual tools containing all functionality that doesn't interact with features
///
/// This is required so that visual tools can be used polymorphically, as
/// different 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
#define VISUAL_SUB_TOOL_END (VISUAL_SUB_TOOL_START+100)
void OnCommit(int type);
void OnSeek(int new_frame);
class IVisualTool : public OpenGLWrapper {
protected:
/// DOCME
static const wxColour colour[4];
public:
virtual void OnMouseEvent(wxMouseEvent &event)=0;
virtual void OnSubTool(wxCommandEvent &)=0;
virtual bool Update()=0;
virtual void Draw()=0;
virtual void Refresh()=0;
virtual void SetFrame(int frame)=0;
virtual ~IVisualTool() { };
};
struct ltaddr {
template<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();
void OnMouseCaptureLost(wxMouseCaptureLostEvent &);
/// @brief Get the dialogue line currently in the edit box
/// @return NULL if the line is not active on the current frame
AssDialogue *GetActiveDialogueLine();
// SubtitleSelectionListener implementation
void OnActiveLineChanged(AssDialogue *new_line);
void OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed) { }
// Below here are the virtuals that must be implemented
/// Called when the script, video or screen resolutions change
virtual void OnCoordinateSystemsChanged() { DoRefresh(); }
/// Called when the file is changed by something other than a visual tool
virtual void OnFileChanged() { DoRefresh(); }
/// Called when the frame number changes
virtual void OnFrameChanged() { }
/// Called when the active line changes
virtual void OnLineChanged() { DoRefresh(); }
/// Generic refresh to simplify tools which have no interesting state and
/// can simply do do the same thing for any external change (i.e. most of
/// them). Called only by the above virtual methods.
virtual void DoRefresh() { }
protected:
OpenGLWrapper gl;
/// Called when the user double-clicks
virtual void OnDoubleClick() { }
static const wxColour colour[4];
agi::Context *c;
VideoDisplay *parent;
bool holding; ///< Is a hold currently in progress?
AssDialogue *active_line; ///< Active dialogue line; NULL if it is not visible on the current frame
bool dragging; ///< Is a drag currently in progress?
int frame_number; ///< Current frame number
bool left_click; ///< Is a left click event currently being processed?
bool left_double; ///< Is a left double click event currently being processed?
bool shift_down; ///< Is shift down?
bool ctrl_down; ///< Is ctrl down?
bool alt_down; ///< Is alt down?
Vector2D mouse_pos; ///< Last seen mouse position
Vector2D drag_start; ///< Mouse position at the beginning of the last drag
Vector2D script_res; ///< Script resolution
Vector2D video_pos; ///< Top-left corner of the video in the display area
Vector2D video_res; ///< Video resolution
agi::signal::Connection file_changed_connection;
int commit_id; ///< Last used commit id for coalescing
/// @brief Commit the current file state
/// @param message Description of changes for undo
void Commit(wxString message = "");
bool IsDisplayed(AssDialogue *line) const;
/// Get the line's position if it's set, or it's default based on style if not
Vector2D GetLinePosition(AssDialogue *diag);
/// Get the line's origin if it's set, or Vector2D::Bad() if not
Vector2D GetLineOrigin(AssDialogue *diag);
bool GetLineMove(AssDialogue *diag, Vector2D &p1, Vector2D &p2, int &t1, int &t2);
void GetLineRotation(AssDialogue *diag, float &rx, float &ry, float &rz);
void GetLineScale(AssDialogue *diag, Vector2D &scale);
void GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse);
wxString GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse);
void SetOverride(AssDialogue* line, wxString const& tag, wxString const& value);
void SetSelectedOverride(wxString const& tag, wxString const& value);
VisualToolBase(VideoDisplay *parent, agi::Context *context);
public:
/// Convert a point from video to script coordinates
Vector2D ToScriptCoords(Vector2D point) const;
/// Convert a point from script to video coordinates
Vector2D FromScriptCoords(Vector2D point) const;
// Stuff called by VideoDisplay
virtual void OnMouseEvent(wxMouseEvent &event)=0;
virtual void Draw()=0;
virtual void SetDisplayArea(int x, int y, int w, int h);
virtual void SetToolbar(wxToolBar *tb) { }
virtual ~VisualToolBase();
};
/// @class VisualTool
/// @brief Visual tool base class containing all common feature-related functionality
/// DOCME
template<class 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;
std::set<feature_iterator, ltaddr> selFeatures; /// Currently selected visual features
std::map<AssDialogue*, int> lineSelCount; /// Number of selected features for each line
bool selChanged; /// Has the selection already been changed in the current click?
bool sel_changed; /// Has the selection already been changed in the current click?
/// @brief Called when a hold is begun
/// @return Should the hold actually happen?
virtual bool InitializeHold() { return false; }
/// @brief Called on every mouse event during a hold
virtual void UpdateHold() { }
/// @brief Called at the end of a hold
virtual void CommitHold() { }
/// @brief Called at the beginning of a drag
/// @param feature The visual feature clicked on
@ -130,67 +193,22 @@ private:
/// @brief Called on every mouse event during a drag
/// @param feature The current feature to process; not necessarily the one clicked on
virtual void UpdateDrag(feature_iterator feature) { }
/// @brief Called at the end of a drag
virtual void CommitDrag(feature_iterator feature) { }
/// Called when the file is changed by something other than a visual tool
virtual void OnFileChanged() { DoRefresh(); }
/// Called when the frame number changes
virtual void OnFrameChanged() { }
/// Called when curDiag changes
virtual void OnLineChanged() { DoRefresh(); }
/// Generic refresh to simplify tools which do the same thing for any
/// external change (i.e. almost all of them). Called only by the above
/// methods.
virtual void DoRefresh() { }
/// @brief Called when there's stuff
/// @return Should the display rerender?
virtual bool Update() { return false; };
/// @brief Draw stuff
virtual void Draw()=0;
protected:
/// Read-only reference to the set of selected features for subclasses
const std::set<feature_iterator, ltaddr> &selectedFeatures;
std::set<feature_iterator, ltaddr> sel_features; ///< Currently selected visual features
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
std::list<FeatureType> features; /// List of features which are drawn and can be clicked on
int frameNumber; /// Current frame number
VideoState const& video; /// Mouse and video information
bool leftClick; /// Is a left click event currently being processed?
bool leftDClick; /// Is a left double click event currently being processed?
bool shiftDown; /// Is shift down?
bool ctrlDown; /// Is ctrl down?
bool altDown; /// Is alt down?
void GetLinePosition(AssDialogue *diag,int &x,int &y);
void GetLinePosition(AssDialogue *diag,int &x,int &y,int &orgx,int &orgy);
void GetLineMove(AssDialogue *diag,bool &hasMove,int &x1,int &y1,int &x2,int &y2,int &t1,int &t2);
void GetLineRotation(AssDialogue *diag,float &rx,float &ry,float &rz);
void GetLineScale(AssDialogue *diag,float &scalX,float &scalY);
void GetLineClip(AssDialogue *diag,int &x1,int &y1,int &x2,int &y2,bool &inverse);
wxString GetLineVectorClip(AssDialogue *diag,int &scale,bool &inverse);
void SetOverride(AssDialogue* line, wxString tag, wxString value);
/// Topmost feature under the mouse; generally only valid during a drag
feature_iterator active_feature;
/// List of features which are drawn and can be clicked on
/// List is used here for the iterator invalidation properties
std::list<FeatureType> features;
/// Draw all of the features in the list
void DrawAllFeatures();
/// @brief Commit the current file state
/// @param message Description of changes for undo
void Commit(wxString message = "");
/// @brief Add a feature (and its line) to the selection
/// @param i Index in the feature list
void AddSelection(feature_iterator feat);
/// @brief Remove a feature from the selection
/// @param i Index in the feature list
@ -199,35 +217,15 @@ protected:
/// @brief Set the selection to a single feature, deselecting everything else
/// @param i Index in the feature list
void SetSelection(feature_iterator feat);
/// @brief Clear the selection
void ClearSelection();
// SubtitleSelectionListener implementation
void OnActiveLineChanged(AssDialogue *new_line);
virtual void OnSelectedSetChanged(const Selection &lines_added, const Selection &lines_removed) { }
void SetSelection(feature_iterator feat, bool clear);
public:
/// @brief Handler for all mouse events
/// @param event Shockingly enough, the mouse event
void OnMouseEvent(wxMouseEvent &event);
/// @brief Event handler for the subtoolbar
virtual void OnSubTool(wxCommandEvent &) { }
/// @brief Signal that the file has changed
void Refresh();
/// @brief Signal that the current frame number has changed
/// @param newFrameNumber The new frame number
void SetFrame(int newFrameNumber);
/// @brief Constructor
/// @param parent The VideoDisplay to use for coordinate conversion
/// @param video Video and mouse information passing blob
VisualTool(VideoDisplay *parent, agi::Context *context, VideoState const& video);
/// @brief Destructor
virtual ~VisualTool();
VisualTool(VideoDisplay *parent, agi::Context *context);
};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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