From 94ad3afab6f2e08f855e90c3e2e0c3717dc1baa3 Mon Sep 17 00:00:00 2001 From: Calixte Denizet Date: Thu, 11 Jan 2024 19:10:29 +0100 Subject: [PATCH] [Editor] Move the code to generate the SVG path from draw_layer.js to outliner.js It'll be useful to avoid too much duplicated code when adding the free highlighting tool. --- src/display/draw_layer.js | 63 ++++++++++++---------------------- src/display/editor/outliner.js | 49 +++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 43 deletions(-) diff --git a/src/display/draw_layer.js b/src/display/draw_layer.js index 1f43e28578b31..3db845d01f2b8 100644 --- a/src/display/draw_layer.js +++ b/src/display/draw_layer.js @@ -69,9 +69,23 @@ class DrawLayer { return svg; } - highlight({ outlines, box }, color, opacity) { + #createClipPath(defs, pathId) { + const clipPath = DrawLayer._svgFactory.createElement("clipPath"); + defs.append(clipPath); + const clipPathId = `clip_${pathId}`; + clipPath.setAttribute("id", clipPathId); + clipPath.setAttribute("clipPathUnits", "objectBoundingBox"); + const clipPathUse = DrawLayer._svgFactory.createElement("use"); + clipPath.append(clipPathUse); + clipPathUse.setAttribute("href", `#${pathId}`); + clipPathUse.classList.add("clip"); + + return clipPathId; + } + + highlight(outlines, color, opacity) { const id = this.#id++; - const root = this.#createSVG(box); + const root = this.#createSVG(outlines.box); root.classList.add("highlight"); const defs = DrawLayer._svgFactory.createElement("defs"); root.append(defs); @@ -79,21 +93,10 @@ class DrawLayer { defs.append(path); const pathId = `path_p${this.pageIndex}_${id}`; path.setAttribute("id", pathId); - path.setAttribute( - "d", - DrawLayer.#extractPathFromHighlightOutlines(outlines) - ); + path.setAttribute("d", outlines.toSVGPath()); // Create the clipping path for the editor div. - const clipPath = DrawLayer._svgFactory.createElement("clipPath"); - defs.append(clipPath); - const clipPathId = `clip_${pathId}`; - clipPath.setAttribute("id", clipPathId); - clipPath.setAttribute("clipPathUnits", "objectBoundingBox"); - const clipPathUse = DrawLayer._svgFactory.createElement("use"); - clipPath.append(clipPathUse); - clipPathUse.setAttribute("href", `#${pathId}`); - clipPathUse.classList.add("clip"); + const clipPathId = this.#createClipPath(defs, pathId); const use = DrawLayer._svgFactory.createElement("use"); root.append(use); @@ -106,13 +109,13 @@ class DrawLayer { return { id, clipPathId: `url(#${clipPathId})` }; } - highlightOutline({ outlines, box }) { + highlightOutline(outlines) { // We cannot draw the outline directly in the SVG for highlights because // it composes with its parent with mix-blend-mode: multiply. // But the outline has a different mix-blend-mode, so we need to draw it in // its own SVG. const id = this.#id++; - const root = this.#createSVG(box); + const root = this.#createSVG(outlines.box); root.classList.add("highlightOutline"); const defs = DrawLayer._svgFactory.createElement("defs"); root.append(defs); @@ -120,10 +123,7 @@ class DrawLayer { defs.append(path); const pathId = `path_p${this.pageIndex}_${id}`; path.setAttribute("id", pathId); - path.setAttribute( - "d", - DrawLayer.#extractPathFromHighlightOutlines(outlines) - ); + path.setAttribute("d", outlines.toSVGPath()); path.setAttribute("vector-effect", "non-scaling-stroke"); const use1 = DrawLayer._svgFactory.createElement("use"); @@ -139,27 +139,6 @@ class DrawLayer { return id; } - static #extractPathFromHighlightOutlines(polygons) { - const buffer = []; - for (const polygon of polygons) { - let [prevX, prevY] = polygon; - buffer.push(`M${prevX} ${prevY}`); - for (let i = 2; i < polygon.length; i += 2) { - const x = polygon[i]; - const y = polygon[i + 1]; - if (x === prevX) { - buffer.push(`V${y}`); - prevY = y; - } else if (y === prevY) { - buffer.push(`H${x}`); - prevX = x; - } - } - buffer.push("Z"); - } - return buffer.join(" "); - } - updateBox(id, box) { DrawLayer.#setBox(this.#mapping.get(id), box); } diff --git a/src/display/editor/outliner.js b/src/display/editor/outliner.js index 9a40cd4ce4b24..077fd71c8e30a 100644 --- a/src/display/editor/outliner.js +++ b/src/display/editor/outliner.js @@ -170,7 +170,7 @@ class Outliner { } outline.push(lastPointX, lastPointY); } - return { outlines, box: this.#box }; + return new HighlightOutline(outlines, this.#box); } #binarySearch(y) { @@ -259,4 +259,51 @@ class Outliner { } } +class Outline { + toSVGPath() { + throw new Error("Abstract method `toSVGPath` must be implemented."); + } + + get box() { + throw new Error("Abstract getter `box` must be implemented."); + } +} + +class HighlightOutline extends Outline { + #box; + + #outlines; + + constructor(outlines, box) { + super(); + this.#outlines = outlines; + this.#box = box; + } + + toSVGPath() { + const buffer = []; + for (const polygon of this.#outlines) { + let [prevX, prevY] = polygon; + buffer.push(`M${prevX} ${prevY}`); + for (let i = 2; i < polygon.length; i += 2) { + const x = polygon[i]; + const y = polygon[i + 1]; + if (x === prevX) { + buffer.push(`V${y}`); + prevY = y; + } else if (y === prevY) { + buffer.push(`H${x}`); + prevX = x; + } + } + buffer.push("Z"); + } + return buffer.join(" "); + } + + get box() { + return this.#box; + } +} + export { Outliner };