Added freehand and smoothed freehand drawing to vector \clip.

Originally committed to SVN as r1386.
This commit is contained in:
Rodrigo Braz Monteiro 2007-07-07 05:51:18 +00:00
parent 5f2508ee70
commit 1af87b0808
16 changed files with 357 additions and 88 deletions

View File

@ -178,6 +178,7 @@ aegisub_SOURCES = \
scintilla_text_ctrl.cpp \
spellchecker.cpp \
spline.cpp \
spline_curve.cpp \
standard_paths.cpp \
static_bmp.cpp \
string_codec.cpp \

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -149,6 +149,7 @@ visual_vector_clip_remove BITMAP "bitmaps/visual_vector_clip_remove.bmp"
visual_vector_clip_convert BITMAP "bitmaps/visual_vector_clip_convert.bmp"
visual_vector_clip_insert BITMAP "bitmaps/visual_vector_clip_insert.bmp"
visual_vector_clip_freehand BITMAP "bitmaps/visual_vector_clip_freehand.bmp"
visual_vector_clip_freehand_smooth BITMAP "bitmaps/visual_vector_clip_freehand_smooth.bmp"
visual_realtime BITMAP "bitmaps/visual_realtime.bmp"
arrow_up BITMAP "bitmaps/arrow_up.bmp"

View File

@ -38,53 +38,7 @@
// Headers
#include <wx/tokenzr.h>
#include "spline.h"
/////////////////////
// Curve constructor
SplineCurve::SplineCurve() {
type = CURVE_INVALID;
}
/////////////////////////////////////////////////////////
// Split a curve in two using the de Casteljau algorithm
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;
c1.p2 = p1*t+p2*(1-t);
c2.p1 = c1.p2;
c2.p2 = p2;
}
// 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*t+p2*u;
Vector2D p23 = p2*t+p3*u;
Vector2D p34 = p3*t+p4*u;
Vector2D p123 = p12*t+p23*u;
Vector2D p234 = p23*t+p34*u;
Vector2D p1234 = p123*t+p234*u;
// Set points
c1.p1 = p1;
c1.p2 = p12;
c1.p3 = p123;
c1.p4 = p1234;
c2.p1 = p1234;
c2.p2 = p234;
c2.p3 = p34;
c2.p4 = p4;
}
}
#include "utils.h"
//////////////////////
@ -362,3 +316,27 @@ Vector2D Spline::GetClosestControlPoint(Vector2D reference) {
// TODO
return Vector2D(-1,-1);
}
///////////////////////
// Smoothes the spline
void Spline::Smooth(float smooth) {
// See if there are enough curves
if (curves.size() < 3) return;
// Smooth curve
SplineCurve *curve0 = NULL;
SplineCurve *curve1 = &curves.back();
SplineCurve *curve2 = NULL;
for (std::list<SplineCurve>::iterator cur=curves.begin();cur!=curves.end();) {
// Get curves
curve0 = curve1;
curve1 = &(*cur);
cur++;
if (cur == curves.end()) curve2 = &curves.front();
else curve2 = &(*cur);
// Smooth curve
curve1->Smooth(curve0->p1,curve2->p2,smooth);
}
}

View File

@ -42,29 +42,7 @@
#include <wx/wxprec.h>
#include <list>
#include <vector>
#include "vector2d.h"
///////////////
// Curve types
enum CurveType {
CURVE_INVALID,
CURVE_POINT,
CURVE_LINE,
CURVE_BICUBIC
};
////////////////
// Spline curve
class SplineCurve {
public:
Vector2D p1,p2,p3,p4;
CurveType type;
SplineCurve();
void Split(SplineCurve &c1,SplineCurve &c2,float t=0.5);
};
#include "spline_curve.h"
/////////////////////////
@ -80,6 +58,7 @@ public:
void AppendCurve(SplineCurve &curve);
void MovePoint(int curveIndex,int point,wxPoint pos);
void Smooth(float smooth=1.0f);
void GetPointList(std::vector<Vector2D> &points);

119
aegisub/spline_curve.cpp Normal file
View File

@ -0,0 +1,119 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// -----------------------------------------------------------------------------
//
// AEGISUB
//
// Website: http://aegisub.cellosoft.com
// Contact: mailto:zeratul@cellosoft.com
//
///////////
// Headers
#include "spline_curve.h"
#include "utils.h"
/////////////////////
// Curve constructor
SplineCurve::SplineCurve() {
type = CURVE_INVALID;
}
/////////////////////////////////////////////////////////
// Split a curve in two using the de Casteljau algorithm
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;
c1.p2 = p1*t+p2*(1-t);
c2.p1 = c1.p2;
c2.p2 = p2;
}
// 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*t+p2*u;
Vector2D p23 = p2*t+p3*u;
Vector2D p34 = p3*t+p4*u;
Vector2D p123 = p12*t+p23*u;
Vector2D p234 = p23*t+p34*u;
Vector2D p1234 = p123*t+p234*u;
// Set points
c1.p1 = p1;
c1.p2 = p12;
c1.p3 = p123;
c1.p4 = p1234;
c2.p1 = p1234;
c2.p2 = p234;
c2.p3 = p34;
c2.p4 = p4;
}
}
//////////////////////
// Smoothes the curve
// Based on http://antigrain.com/research/bezier_interpolation/index.html
void SplineCurve::Smooth(Vector2D P0,Vector2D P3,float smooth) {
// Validate
if (type != CURVE_LINE) return;
smooth = MID(0.0f,smooth,1.0f);
// Get points
Vector2D P1 = p1;
Vector2D P2 = p2;
// Calculate intermediate points
Vector2D c1 = (P0+P1)/2.0f;
Vector2D c2 = (P1+P2)/2.0f;
Vector2D c3 = (P2+P3)/2.0f;
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;
}

65
aegisub/spline_curve.h Normal file
View File

@ -0,0 +1,65 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// -----------------------------------------------------------------------------
//
// AEGISUB
//
// Website: http://aegisub.cellosoft.com
// Contact: mailto:zeratul@cellosoft.com
//
#pragma once
///////////
// Headers
#include "vector2d.h"
///////////////
// Curve types
enum CurveType {
CURVE_INVALID,
CURVE_POINT,
CURVE_LINE,
CURVE_BICUBIC
};
////////////////
// Spline curve
class SplineCurve {
public:
Vector2D p1,p2,p3,p4;
CurveType type;
SplineCurve();
void Split(SplineCurve &c1,SplineCurve &c2,float t=0.5);
void Smooth(Vector2D prev,Vector2D next,float smooth=1.0f);
};

View File

@ -64,9 +64,10 @@ public:
Vector2D Unit ();
float Cross (const Vector2D param) const;
virtual float Dot (const Vector2D param) const;
float Dot (const Vector2D param) const;
virtual float Len () const;
float Len () const;
float Length () const { return Len(); }
};

View File

@ -56,6 +56,7 @@
#include "utils.h"
#include "main.h"
#include "toggle_bitmap.h"
#include "visual_tool.h"
///////////////
@ -157,6 +158,7 @@ BEGIN_EVENT_TABLE(VideoBox, wxPanel)
EVT_TOGGLEBUTTON(Video_Auto_Scroll, VideoBox::OnVideoToggleScroll)
EVT_TOOL_RANGE(Video_Mode_Standard, Video_Mode_Vector_Clip, VideoBox::OnModeChange)
EVT_TOOL_RANGE(VISUAL_SUB_TOOL_START,VISUAL_SUB_TOOL_END, VideoBox::OnSubTool)
EVT_TOOL(Video_Mode_Realtime, VideoBox::OnToggleRealtime)
END_EVENT_TABLE()
@ -201,6 +203,13 @@ void VideoBox::OnModeChange(wxCommandEvent &event) {
}
///////////////////////////
// Sub-tool button pressed
void VideoBox::OnSubTool(wxCommandEvent &event) {
videoDisplay->visual->OnSubTool(event);
}
///////////////////
// Realtime toggle
void VideoBox::OnToggleRealtime(wxCommandEvent &event) {

View File

@ -34,8 +34,7 @@
//
#ifndef VIDEO_BOX_H
#define VIDEO_BOX_H
#pragma once
///////////
@ -62,6 +61,7 @@ private:
void OnVideoToggleScroll(wxCommandEvent &event);
void OnModeChange(wxCommandEvent &event);
void OnSubTool(wxCommandEvent &event);
void OnToggleRealtime(wxCommandEvent &event);
public:
@ -102,5 +102,3 @@ enum {
Video_Mode_Vector_Clip,
Video_Mode_Realtime,
};
#endif

View File

@ -132,8 +132,16 @@ void VisualTool::OnMouseEvent (wxMouseEvent &event) {
dragListOK = true;
}
// Click on feature
if (!dragging && leftClick && !DragEnabled()) {
curFeature = GetHighlightedFeature();
if (curFeature != -1) {
ClickedFeature(features[curFeature]);
}
}
// Start dragging
if (!dragging && leftClick) {
if (!dragging && leftClick && DragEnabled()) {
// Get a feature
curFeature = GetHighlightedFeature();
if (curFeature != -1) {
@ -178,13 +186,13 @@ void VisualTool::OnMouseEvent (wxMouseEvent &event) {
if (realTime) AssLimitToVisibleFilter::SetFrame(-1);
// Commit
dragging = false;
CommitDrag(features[curFeature]);
grid->editBox->CommitText();
grid->ass->FlagAsModified(_("visual typesetting"));
grid->CommitChanges(false);
// Clean up
dragging = false;
curFeature = -1;
parent->ReleaseMouse();
parent->SetFocus();
@ -196,7 +204,7 @@ void VisualTool::OnMouseEvent (wxMouseEvent &event) {
// Hold
if (!dragging && CanHold()) {
// Start holding
if (!holding && event.LeftIsDown()) {
if (!holding && event.LeftIsDown() && HoldEnabled()) {
// Get a dialogue
curDiag = GetActiveDialogueLine();
if (curDiag) {
@ -230,13 +238,13 @@ void VisualTool::OnMouseEvent (wxMouseEvent &event) {
if (realTime) AssLimitToVisibleFilter::SetFrame(-1);
// Commit
holding = false;
CommitHold();
grid->editBox->CommitText();
grid->ass->FlagAsModified(_("visual typesetting"));
grid->CommitChanges(false);
// Clean up
holding = false;
curDiag = NULL;
parent->ReleaseMouse();
parent->SetFocus();

View File

@ -52,6 +52,12 @@ class AssDialogue;
class VisualTool;
/////////////////////////
// Visual sub tool range
#define VISUAL_SUB_TOOL_START 1300
#define VISUAL_SUB_TOOL_END (VISUAL_SUB_TOOL_START+100)
////////////////////
// Event sink class
class VisualToolEvent : public wxEvtHandler {
@ -114,15 +120,18 @@ protected:
virtual void OnButton(wxCommandEvent &event) {}
virtual bool CanHold() { return false; }
virtual bool HoldEnabled() { return true; }
virtual void InitializeHold() {}
virtual void UpdateHold() {}
virtual void CommitHold() {}
virtual bool CanDrag() { return false; }
virtual bool DragEnabled() { return true; }
virtual void PopulateFeatureList() { wxLogMessage(_T("wtf?")); }
virtual void InitializeDrag(VisualDraggableFeature &feature) {}
virtual void UpdateDrag(VisualDraggableFeature &feature) {}
virtual void CommitDrag(VisualDraggableFeature &feature) {}
virtual void ClickedFeature(VisualDraggableFeature &feature) {}
virtual void DoRefresh() {}
@ -130,6 +139,7 @@ public:
int mouseX,mouseY;
void OnMouseEvent(wxMouseEvent &event);
virtual void OnSubTool(wxCommandEvent &event) {}
virtual void Update()=0;
virtual void Draw()=0;
void Refresh();

View File

@ -51,7 +51,7 @@
///////
// IDs
enum {
BUTTON_TOGGLE_MOVE = 1300
BUTTON_TOGGLE_MOVE = VISUAL_SUB_TOOL_START
};

View File

@ -43,24 +43,28 @@
///////
// IDs
enum {
BUTTON_DRAG = 1300,
BUTTON_DRAG = VISUAL_SUB_TOOL_START,
BUTTON_LINE,
BUTTON_BICUBIC,
BUTTON_INSERT,
BUTTON_REMOVE,
BUTTON_CONVERT,
BUTTON_FREEHAND
BUTTON_FREEHAND,
BUTTON_FREEHAND_SMOOTH,
BUTTON_LAST // Leave this at the end and don't use it
};
///////////////
// Constructor
VisualToolVectorClip::VisualToolVectorClip(VideoDisplay *parent,wxToolBar *toolBar)
VisualToolVectorClip::VisualToolVectorClip(VideoDisplay *parent,wxToolBar *_toolBar)
: VisualTool(parent)
{
DoRefresh();
mode = 0;
// Create toolbar
toolBar = _toolBar;
toolBar->AddTool(BUTTON_DRAG,_("Drag"),wxBITMAP(visual_vector_clip_drag),_("Drag control points."),wxITEM_CHECK);
toolBar->AddTool(BUTTON_LINE,_("Line"),wxBITMAP(visual_vector_clip_line),_("Appends a line."),wxITEM_CHECK);
toolBar->AddTool(BUTTON_BICUBIC,_("Bicubic"),wxBITMAP(visual_vector_clip_bicubic),_("Appends a bezier bicubic curve."),wxITEM_CHECK);
@ -70,11 +74,31 @@ VisualToolVectorClip::VisualToolVectorClip(VideoDisplay *parent,wxToolBar *toolB
toolBar->AddTool(BUTTON_REMOVE,_("Remove"),wxBITMAP(visual_vector_clip_remove),_("Removes a control point."),wxITEM_CHECK);
toolBar->AddSeparator();
toolBar->AddTool(BUTTON_FREEHAND,_("Freehand"),wxBITMAP(visual_vector_clip_freehand),_("Draws a freehand shape."),wxITEM_CHECK);
toolBar->AddTool(BUTTON_FREEHAND_SMOOTH,_("Freehand smooth"),wxBITMAP(visual_vector_clip_freehand_smooth),_("Draws a smoothed freehand shape."),wxITEM_CHECK);
toolBar->ToggleTool(BUTTON_DRAG,true);
toolBar->Realize();
toolBar->Show(true);
}
////////////////////
// Sub-tool pressed
void VisualToolVectorClip::OnSubTool(wxCommandEvent &event) {
// Make sure clicked is checked and everything else isn't. (Yes, this is radio behavior, but the separators won't let me use it)
for (int i=BUTTON_DRAG;i<BUTTON_LAST;i++) {
toolBar->ToggleTool(i,i == event.GetId());
}
SetMode(event.GetId() - BUTTON_DRAG);
}
////////////
// Set mode
void VisualToolVectorClip::SetMode(int _mode) {
mode = _mode;
}
//////////
// Update
void VisualToolVectorClip::Update() {
@ -96,8 +120,8 @@ void VisualToolVectorClip::Draw() {
// Draw lines
SetLineColour(colour[3],1.0f,2);
SetFillColour(colour[3],0.0f);
for (int i=0;i<((signed)points.size())-1;i++) {
DrawLine(points[i].x,points[i].y,points[i+1].x,points[i+1].y);
for (size_t i=1;i<points.size();i++) {
DrawLine(points[i-1].x,points[i-1].y,points[i].x,points[i].y);
}
// Draw stencil mask
@ -105,11 +129,11 @@ void VisualToolVectorClip::Draw() {
glColorMask(0,0,0,0);
glStencilFunc(GL_NEVER,1,1);
glStencilOp(GL_INVERT,GL_INVERT,GL_INVERT);
for (int i=0;i<((signed)points.size())-1;i++) {
for (size_t i=2;i<points.size();i++) {
glBegin(GL_TRIANGLES);
glVertex2f(points[0].x,points[0].y);
glVertex2f(points[i-1].x,points[i-1].y);
glVertex2f(points[i].x,points[i].y);
glVertex2f(points[i+1].x,points[i+1].y);
glEnd();
}
@ -198,6 +222,13 @@ void VisualToolVectorClip::PopulateFeatureList() {
}
/////////////
// Can drag?
bool VisualToolVectorClip::DragEnabled() {
return mode == 0;
}
//////////
// Update
void VisualToolVectorClip::UpdateDrag(VisualDraggableFeature &feature) {
@ -212,6 +243,61 @@ void VisualToolVectorClip::CommitDrag(VisualDraggableFeature &feature) {
}
/////////////////////
// Clicked a feature
void VisualToolVectorClip::ClickedFeature(VisualDraggableFeature &feature) {
}
/////////////
// Can hold?
bool VisualToolVectorClip::HoldEnabled() {
return mode == 6 || mode == 7;
}
///////////////////
// Initialize hold
void VisualToolVectorClip::InitializeHold() {
spline.curves.clear();
lastX = -100000;
lastY = -100000;
}
///////////////
// Update hold
void VisualToolVectorClip::UpdateHold() {
if (lastX != -100000 && lastY != -100000) {
// See if distance is enough
Vector2D delta(lastX-mx,lastY-my);
int len = (int)delta.Len();
if (mode == 6 && len < 30) return;
if (mode == 7 && len < 60) return;
// Generate curve and add it
SplineCurve curve;
curve.type = CURVE_LINE;
curve.p1 = Vector2D(lastX,lastY);
curve.p2 = Vector2D(mx,my);
spline.AppendCurve(curve);
}
lastX = mx;
lastY = my;
}
///////////////
// Commit hold
void VisualToolVectorClip::CommitHold() {
// Smooth spline
if (!holding && mode == 7) spline.Smooth();
// Save it
SetOverride(_T("\\clip"),_T("(") + spline.EncodeToASS() + _T(")"));
}
///////////
// Refresh
void VisualToolVectorClip::DoRefresh() {

View File

@ -48,13 +48,27 @@
class VisualToolVectorClip : public VisualTool {
private:
Spline spline;
wxToolBar *toolBar;
int mode;
int lastX,lastY;
void SetMode(int mode);
bool CanHold() { return true; }
bool HoldEnabled();
void InitializeHold();
void UpdateHold();
void CommitHold();
bool CanDrag() { return true; }
bool DragEnabled();
void PopulateFeatureList();
void UpdateDrag(VisualDraggableFeature &feature);
void CommitDrag(VisualDraggableFeature &feature);
void ClickedFeature(VisualDraggableFeature &feature);
void DoRefresh();
void OnSubTool(wxCommandEvent &event);
public:
VisualToolVectorClip(VideoDisplay *parent,wxToolBar *toolbar);