From 20cc0b807768e4078696e88432e0e5f902378002 Mon Sep 17 00:00:00 2001 From: arch1t3cht Date: Sat, 29 Apr 2023 01:11:38 +0200 Subject: [PATCH] perspective tool: Support drawings This will not work in more complex cases like lines containing both text and drawings, but it's correct in simpler ones. --- src/spline.cpp | 11 +++-- src/spline.h | 5 ++- src/spline_curve.cpp | 9 ++++ src/spline_curve.h | 1 + src/visual_tool.cpp | 76 +++++++++++++++++++++++---------- src/visual_tool.h | 9 +++- src/visual_tool_perspective.cpp | 32 +++++++------- src/visual_tool_perspective.h | 7 +-- src/visual_tool_vector_clip.cpp | 2 +- 9 files changed, 104 insertions(+), 48 deletions(-) diff --git a/src/spline.cpp b/src/spline.cpp index 395bac084..97eb31bc8 100644 --- a/src/spline.cpp +++ b/src/spline.cpp @@ -41,17 +41,22 @@ #include -Spline::Spline(const VisualToolBase &tl) +Spline::Spline(const VisualToolBase *tl) : coord_translator(tl) { } Vector2D Spline::ToScript(Vector2D vec) const { - return coord_translator.ToScriptCoords(vec) * scale; + if (coord_translator) + vec = coord_translator->ToScriptCoords(vec); + return vec * scale; } Vector2D Spline::FromScript(Vector2D vec) const { - return coord_translator.FromScriptCoords(vec / scale); + vec = vec / scale; + if (coord_translator) + vec = coord_translator->FromScriptCoords(vec); + return vec; } void Spline::SetScale(int new_scale) { diff --git a/src/spline.h b/src/spline.h index 2594837e8..106df1d7d 100644 --- a/src/spline.h +++ b/src/spline.h @@ -36,7 +36,7 @@ class VisualToolBase; class Spline final : private std::vector { /// Visual tool to do the conversion between script and video pixels - const VisualToolBase &coord_translator; + const VisualToolBase *coord_translator = nullptr; /// Spline scale int scale = 0; int raw_scale = 0; @@ -47,7 +47,8 @@ class Spline final : private std::vector { /// Script coordinates -> Video coordinates Vector2D FromScript(Vector2D vec) const; public: - Spline(const VisualToolBase &scale); + Spline() {}; + Spline(const VisualToolBase *scale); /// Encode to an ASS vector drawing std::string EncodeToAss() const; diff --git a/src/spline_curve.cpp b/src/spline_curve.cpp index b1a799a0f..e59010124 100644 --- a/src/spline_curve.cpp +++ b/src/spline_curve.cpp @@ -115,6 +115,15 @@ Vector2D& SplineCurve::EndPoint() { } } +std::vector SplineCurve::AnchorPoints() { + switch (type) { + case POINT: return std::vector({p1}); + case LINE: return std::vector({p1, p2}); + case BICUBIC: return std::vector({p1, p2, p3, p4}); + default: return std::vector(); + } +} + Vector2D SplineCurve::GetClosestPoint(Vector2D ref) const { return GetPoint(GetClosestParam(ref)); } diff --git a/src/spline_curve.h b/src/spline_curve.h index 4630a64d2..59a713574 100644 --- a/src/spline_curve.h +++ b/src/spline_curve.h @@ -73,6 +73,7 @@ public: Vector2D GetPoint(float t) const; Vector2D& EndPoint(); + std::vector AnchorPoints(); /// Get point on the curve closest to reference Vector2D GetClosestPoint(Vector2D ref) const; /// Get t value for the closest point to reference diff --git a/src/visual_tool.cpp b/src/visual_tool.cpp index e55c3b88f..c6da1fa25 100644 --- a/src/visual_tool.cpp +++ b/src/visual_tool.cpp @@ -40,6 +40,8 @@ #include #include +#include +#include #include #include @@ -536,11 +538,9 @@ int VisualToolBase::GetLineAlignment(AssDialogue *diag) { return an; } -void VisualToolBase::GetLineBaseExtents(AssDialogue *diag, double &width, double &height, double &descent, double &extlead) { - width = 0.; - height = 0.; - descent = 0.; - extlead = 0.; +std::pair VisualToolBase::GetLineBaseExtents(AssDialogue *diag) { + double width = 0.; + double height = 0.; AssStyle style; if (AssStyle *basestyle = c->ass->GetStyle(diag->Style)) { @@ -550,25 +550,57 @@ void VisualToolBase::GetLineBaseExtents(AssDialogue *diag, double &width, double } auto blocks = diag->ParseTags(); - if (param_vec tag = find_tag(blocks, "\\fs")) - style.fontsize = tag->front().Get(style.fontsize); - if (param_vec tag = find_tag(blocks, "\\fn")) - style.font = tag->front().Get(style.font); + param_vec ptag = find_tag(blocks, "\\p"); - std::string text = diag->GetStrippedText(); - std::vector textlines; - boost::replace_all(text, "\\N", "\n"); - agi::Split(textlines, text, '\n'); - for (std::string line : textlines) { - double linewidth = 0; - double lineheight = 0; - if (!Automation4::CalculateTextExtents(&style, line, linewidth, lineheight, descent, extlead)) { - // meh... let's make some ballpark estimates - linewidth = style.fontsize * line.length(); - lineheight = style.fontsize; + if (ptag && ptag->front().Get(0)) { // A drawing + Spline spline; + spline.SetScale(ptag->front().Get(1)); + std::string drawing_text = join(blocks | agi::of_type() | boost::adaptors::transformed([&](AssDialogueBlock *d) { return d->GetText(); }), ""); + spline.DecodeFromAss(drawing_text); + + if (!spline.size()) + return std::make_pair(Vector2D(0, 0), Vector2D(0, 0)); + + float left = FLT_MAX; + float top = FLT_MAX; + float right = -FLT_MAX; + float bot = -FLT_MAX; + + for (SplineCurve curve : spline) { + for (Vector2D pt : curve.AnchorPoints()) { + left = std::min(left, pt.X()); + top = std::min(top, pt.Y()); + right = std::max(right, pt.X()); + bot = std::max(bot, pt.Y()); + } } - width = std::max(width, linewidth); - height += lineheight; + + return std::make_pair(Vector2D(left, top), Vector2D(right, bot)); + } else { + if (param_vec tag = find_tag(blocks, "\\fs")) + style.fontsize = tag->front().Get(style.fontsize); + if (param_vec tag = find_tag(blocks, "\\fn")) + style.font = tag->front().Get(style.font); + + std::string text = diag->GetStrippedText(); + std::vector textlines; + boost::replace_all(text, "\\N", "\n"); + agi::Split(textlines, text, '\n'); + for (std::string line : textlines) { + double linewidth = 0; + double lineheight = 0; + + double descent; + double extlead; + if (!Automation4::CalculateTextExtents(&style, line, linewidth, lineheight, descent, extlead)) { + // meh... let's make some ballpark estimates + linewidth = style.fontsize * line.length(); + lineheight = style.fontsize; + } + width = std::max(width, linewidth); + height += lineheight; + } + return std::make_pair(Vector2D(0, 0), Vector2D(width, height)); } } diff --git a/src/visual_tool.h b/src/visual_tool.h index e9e8770b9..0ddc6db58 100644 --- a/src/visual_tool.h +++ b/src/visual_tool.h @@ -132,11 +132,18 @@ protected: float GetLineFontSize(AssDialogue *diag); int GetLineAlignment(AssDialogue *diag); /// @brief Compute text extents of the given line without any formatting + /// @param diag The dialogue line + /// @return The top left and bottom right corners of the line's bounding box respectively. /// /// Formatting tags are stripped and \fs tags are respected, but \fscx and \fscy are kept as 100 even if /// they are different in the style. + /// For text the top left corner of the bounding box will always be at the origin, but this needn't be + /// the case for drawings. The width and height of the bounding box are the shifts used for text alignment. + /// + /// This function works for most common line formats, but can be inaccurate for more complex cases such as lines + /// containing both text and drawings. /// Returns a rough estimate when getting the precise extents fails - void GetLineBaseExtents(AssDialogue *diag, double &width, double &height, double &descent, double &extlead); + std::pair GetLineBaseExtents(AssDialogue *diag); void GetLineClip(AssDialogue *diag, Vector2D &p1, Vector2D &p2, bool &inverse); std::string GetLineVectorClip(AssDialogue *diag, int &scale, bool &inverse); diff --git a/src/visual_tool_perspective.cpp b/src/visual_tool_perspective.cpp index 86d629d96..deb0f7189 100644 --- a/src/visual_tool_perspective.cpp +++ b/src/visual_tool_perspective.cpp @@ -334,7 +334,7 @@ void VisualToolPerspective::Draw() { gl.SetRotation(angle_x, angle_y, angle_z); gl.SetScale(fsc); gl.SetShear(fax, fay); - Vector2D glScale = textheight * Vector2D(1, 1) / spacing / 4; + Vector2D glScale = (bbox.second.Y() - bbox.first.Y()) * Vector2D(1, 1) / spacing / 4; gl.SetScale(100 * glScale); // Draw grid @@ -680,17 +680,18 @@ bool VisualToolPerspective::InnerToText() { float quadwidth = ab.Len(); float quadheight = abs(ad.Y()); - float scalex = quadwidth / textwidth; - float scaley = quadheight / textheight; + float scalex = quadwidth / std::max(bbox.second.X() - bbox.first.X(), 1.0f); + float scaley = quadheight / std::max(bbox.second.Y() - bbox.first.Y(), 1.0f); + Vector2D scale = Vector2D(scalex, scaley); float shiftv = align <= 3 ? 1 : (align <= 6 ? 0.5 : 0); float shifth = align % 3 == 0 ? 1 : (align % 3 == 2 ? 0.5 : 0); - pos = org + r[0].XY() + Vector2D(quadwidth * shifth, quadheight * shiftv); + pos = org + r[0].XY() - bbox.first * scale + Vector2D(quadwidth * shifth, quadheight * shiftv); angle_x = rotx * rad2deg; angle_y = -roty * rad2deg; angle_z = -rotz * rad2deg; Vector2D oldfsc = fsc; - fsc = 100 * Vector2D(scalex, scaley); + fsc = 100 * scale; fax = rawfax * scaley / scalex; fay = 0; @@ -781,40 +782,39 @@ void VisualToolPerspective::TextToPersp() { align = GetLineAlignment(active_line); - double descend, extlead; - GetLineBaseExtents(active_line, textwidth, textheight, descend, extlead); - textwidth = std::max(textwidth, 1.); - textheight = std::max(textheight, 1.); - double textleft = 0., texttop = 0.; + bbox = GetLineBaseExtents(active_line); + float textwidth = std::max(bbox.second.X() - bbox.first.X(), 1.f); + float textheight = std::max(bbox.second.Y() - bbox.first.Y(), 1.f); + double shiftx = 0., shifty = 0.; switch ((align - 1) % 3) { case 1: - textleft = -textwidth / 2; + shiftx = -textwidth / 2; break; case 2: - textleft = -textwidth; + shiftx = -textwidth; break; default: break; } switch ((align - 1) / 3) { case 0: - texttop = -textheight; + shifty = -textheight; break; case 1: - texttop = -textheight / 2; + shifty = -textheight / 2; break; default: break; } - std::vector textrect = MakeRect(Vector2D(0, 0), Vector2D(textwidth, textheight)); + std::vector textrect = MakeRect(bbox.first, bbox.second); for (int i = 0; i < 4; i++) { Vector2D p = textrect[i]; // Apply \fax and \fay p = Vector2D(p.X() + p.Y() * fax, p.X() * fay + p.Y()); // Translate to alignment point - p = p + Vector2D(textleft, texttop); + p = p + Vector2D(shiftx, shifty); // Apply scaling p = Vector2D(p.X() * fsc.X() / 100., p.Y() * fsc.Y() / 100.); // Translate relative to origin diff --git a/src/visual_tool_perspective.h b/src/visual_tool_perspective.h index 82647a7b6..3834f12cb 100644 --- a/src/visual_tool_perspective.h +++ b/src/visual_tool_perspective.h @@ -52,7 +52,7 @@ public: class VisualToolPerspective final : public VisualTool { wxToolBar *toolBar = nullptr; /// The subtoolbar - int settings = 0; + int settings = 0; agi::OptionValue* optOuter; agi::OptionValue* optOuterLocked; @@ -69,8 +69,9 @@ class VisualToolPerspective final : public VisualTool bbox; Vector2D fsc; diff --git a/src/visual_tool_vector_clip.cpp b/src/visual_tool_vector_clip.cpp index d90c4eec9..5397001ec 100644 --- a/src/visual_tool_vector_clip.cpp +++ b/src/visual_tool_vector_clip.cpp @@ -34,7 +34,7 @@ int BUTTON_ID_BASE = 1300; VisualToolVectorClip::VisualToolVectorClip(VideoDisplay *parent, agi::Context *context) : VisualTool(parent, context) -, spline(*this) +, spline(this) { }