SVG или Canvas bevel & emboss для нахождения центральной линии текста - PullRequest
0 голосов
/ 07 октября 2018

Эффект скоса и тиснения в фотошопе позволяет легко найти центральный «гребень», который совпадает с центральной линией текстовых символов.Это делается путем увеличения соответствующих настроек эффекта, чтобы максимизировать скос, таким образом создавая такой гребень.

Photoshop example

Этот пример Photoshop былобработан для дальнейшего выделения центрального гребня

Можно ли добиться того же эффекта с помощью фильтра SVG или техники Canvas в браузере?

Как только этот эффект будет на месте, я мог быполучить координаты центральной линии, которую я хочу.

В качестве альтернативы, существует ли существующий алгоритм для получения этой центральной линии математическим путем из растрового изображения или векторной формы?

Ответы [ 2 ]

0 голосов
/ 08 октября 2018

SVG фильтры - это мощная функция, которая может быть похожа на фотошоп в браузере.Вы можете достичь желаемого результата, связав несколько примитивов фильтра вместе.

<filter id="filterData">
    <feGaussianBlur stdDeviation="5" />
    <feDiffuseLighting surfaceScale="500">
         <feDistantLight azimuth="90" elevation="90" />
    </feDiffuseLighting>
    <feComposite result="composite" operator="in" in2="SourceGraphic" /> 
</filter>

Первый примитив размывает текст.Затем примитив освещения использует результат размытого примитива в качестве карты рельефа, чтобы придать глубину текста.Вам придется поиграть с атрибутом surfaceScale в зависимости от толщины текста.Составной примитив обрежет конечный результат до области нефильтрованного текста, «SourceGraphic».

[пример кода] https://codepen.io/lahaymd/pen/EdNXam

0 голосов
/ 08 октября 2018

Каким-то образом это пощекотало мое воображение, хотя я не уверен, что это эффективный способ получить результат.

Что такое центральная линия?Я определяю его как набор всех точек внутри контура, которые удовлетворяют следующему условию: должна быть хотя бы одна прямая линия, проходящая через точку, в которой расстояние до ближайшей линии контура является локальным максимумом вдоль линии как раз в этой точке.На практике достаточно тестирования горизонтальной и вертикальной линий.

Я пытался реализовать это, используя две функции из интерфейса SVGGeometryElement : .getPointAtLength() и .isPointInFill().Второй до сих пор был реализован только в Chrome, так что это единственный браузер, с которым он будет работать.

Элемент <text> не реализует интерфейс SVGGeometryElement, поэтому его необходимо преобразовать в<path>.Это то, что нельзя сделать в браузере, для этого вам понадобится соответствующая графическая программа.

Поиск за 1000 * 500 баллов, что за ок.5000 точек по контуру двух букв является ближайшим, это много вычислений.Следовательно, это содержит грубый механизм для проверки только тех точек контура, которые находятся в окрестности.Тем не менее, дайте ему несколько секунд для завершения.Если вы вычислите только одну букву с таким размером и уменьшите размер холста пополам, время выполнения составит примерно четверть.

const width = 1000;
const height = 500;

const letter = document.querySelector('path');
const svg = document.querySelector('svg');
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'white';

function isInside(x, y) {
    const point = svg.createSVGPoint();
    point.x = x;
    point.y = y;
    return letter.isPointInFill(point);
}

// a 21 * 11 array of arrays
const fields = new Array(21).fill(0).map(() => {
    return new Array(11).fill(0).map(() => []);
});

// a list of points along the contour
const length = Math.floor(letter.getTotalLength());
Array.from(new Array(length), (x, i) => {
    return letter.getPointAtLength(i);
}).forEach(point => {
    // find out if a contour point is inside a 100 * 100 rectangle
    let rx1= Math.round(point.x / 100) * 2;
    let ry1 = Math.round(point.y / 100) * 2;
    // or a 100 * 100 rectangle that is offset by 50
    let rx2 = Math.round((point.x + 50) / 100) * 2 - 1;
    let ry2 = Math.round((point.y + 50) / 100) * 2 - 1;
    // push the point into all four lists for the rectangles it is part of
    fields[rx1][ry1].push(point);
    fields[rx1][ry2].push(point);
    fields[rx2][ry1].push(point);
    fields[rx2][ry2].push(point);
});

const data = new Float32Array(width * height);

for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
        // only handle points inside the contour
        if (isInside(x, y)) {
            // find out which 50 * 50 rectangle the inside point is part of
            const rx = Math.round(x / 50);
            const ry = Math.round(y / 50);
            // find the nearest contour point from the list for the
            // appropriate 100 * 100 rectangle
            const d = fields[rx][ry].reduce((min, point) => {
                const dist = Math.hypot(point.x - x, point.y - y)
                return Math.min(min, dist);
            }, 100);
            // store that distance value
            data[y * width + x] = d;
        }
    }
}

data.forEach((v, i, a) => {
    // find out if the distance to the nearest contour point
    // is a local maximum, vertically or horizontally
    const vert = a[i - width] < v && a[i + width] < v;
    const hor = a[i - 1] < v && a[i + 1] < v;
    if (vert || hor) {
        // color that point as part of the center line
        ctx.fillRect(i % width, Math.floor(i / width), 1, 1);
    }
});
<svg width="1000" height="500" style="position:absolute">
    <path id="letter" d="M 374.512,316.992 H 220.703 L 193.75,379.687 Q 183.789,402.832 183.789,414.258 183.789,423.34 192.285,430.371 201.074,437.109 229.785,439.16 V 450 H 104.688 V 439.16 Q 129.59,434.766 136.914,427.734 151.855,413.672 170.02,370.605 L 309.766,43.6523 H 320.02 L 458.301,374.121 Q 475,413.965 488.477,425.977 502.246,437.695 526.562,439.16 V 450 H 369.824 V 439.16 Q 393.555,437.988 401.758,431.25 410.254,424.512 410.254,414.844 410.254,401.953 398.535,374.121 Z M 366.309,295.312 298.926,134.766 229.785,295.312 Z M 810.742,247.266 Q 852.051,256.055 872.559,275.391 900.977,302.344 900.977,341.309 900.977,370.898 882.227,398.145 863.477,425.098 830.664,437.695 798.145,450 731.055,450 H 543.555 V 439.16 H 558.496 Q 583.398,439.16 594.238,423.34 600.977,413.086 600.977,379.687 V 123.047 Q 600.977,86.1328 592.48,76.4648 581.055,63.5742 558.496,63.5742 H 543.555 V 52.7344 H 715.234 Q 763.281,52.7344 792.285,59.7656 836.23,70.3125 859.375,97.2656 882.52,123.926 882.52,158.789 882.52,188.672 864.355,212.402 846.191,235.84 810.742,247.266 Z M 657.227,231.445 Q 668.066,233.496 681.836,234.668 695.898,235.547 712.598,235.547 755.371,235.547 776.758,226.465 798.437,217.09 809.863,198.047 821.289,179.004 821.289,156.445 821.289,121.582 792.871,96.9727 764.453,72.3633 709.961,72.3633 680.664,72.3633 657.227,78.8086 Z M 657.227,421.289 Q 691.211,429.199 724.316,429.199 777.344,429.199 805.176,405.469 833.008,381.445 833.008,346.289 833.008,323.145 820.41,301.758 807.812,280.371 779.395,268.066 750.977,255.762 709.082,255.762 690.918,255.762 678.027,256.348 665.137,256.934 657.227,258.398 Z"/>
</svg>
<canvas width="1000" height="500" style="position:absolute"></canvas>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...