Вырезание жирного удара

Я хочу нарисовать это: введите здесь описание изображенияДве линии, идущие вдоль пути бейзера на одинаковом расстоянии. Жирные линии — это то, что я хочу, маленькая пунктирная — это направляющая дорожка.

Изображение выше сделано путем сначала обводки контура шириной 30, а затем шириной 25 того же цвета, что и фон. Иногда это работает нормально, но не в том случае, если фон не однотонный.

Я хочу что-то вроде клипа с формой. И я бы предпочел сделать это с помощью стандартных графических библиотек. Еще лучше инструменты, которые входят в большинство полотен для 2D-графики.

Примечания:

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

person Thomas Ahle    schedule 16.05.2012    source источник
comment
Вопрос хоть и подробно описан, но не ясен. Программно это просто два параллельных сплайна Безье, смещенных от исходного сплайна. С точки зрения виджета, инструмент, который брал бы физически нарисованный эталонный сплайн и создавал параллельные сплайны. Обрезать? не уверен, что вы имеете в виду там.   -  person Dtyree    schedule 16.05.2012
comment
Я не знаю бейзеров в боках. Только тот, что посередине. Не очевидно, как я могу найти внешние.   -  person Thomas Ahle    schedule 17.05.2012
comment
Смотрите мой ответ ниже. Вам нужно знать только средний.   -  person Dtyree    schedule 24.05.2012


Ответы (2)


Я могу придумать способ сделать это в HTML5 Canvas.

Что вам нужно сделать, так это нарисовать кривую на временном холсте в памяти, а затем нарисовать ту же кривую с меньшей толщиной и с globalCompositeOperation, установленным на destination-out, на том же холсте.

Это даст вам нужную форму, по сути, две линии с прозрачностью между ними.

Затем вы рисуете этот холст на реальном холсте, на котором есть что-либо (сложный фон и т. д.).

Вот пример:

http://jsfiddle.net/at3zR/

person Simon Sarris    schedule 16.05.2012
comment
Вау, это очень здорово! Я понятия не имел, что у холста есть такие функции. - person Thomas Ahle; 17.05.2012
comment
Он даже существует в PlayN (через который я управляю холстом): docs.playn.googlecode.com/git/javadoc/playn/core/ - person Thomas Ahle; 17.05.2012

Следующее должно лучше проиллюстрировать то, что я имел в виду. Существует простой алгоритм, который необходимо добавить для настройки смещения в зависимости от того, где расположены контрольные точки по отношению друг к другу. Если у меня будет больше времени и не забуду, я добавлю.

bezier.js
/* 
 * 
 * This code was Shamlessly stolen from:
 * Canvas curves example
 *
 * By Craig Buckler,        http://twitter.com/craigbuckler
 * of OptimalWorks.net      http://optimalworks.net/
 * for SitePoint.com        http://sitepoint.com/
 * 
 * Refer to:
 * http://blogs.sitepoint.com/html5-canvas-draw-quadratic-curves/
 * http://blogs.sitepoint.com/html5-canvas-draw-bezier-curves/
 *
 * This code can be used without restriction. 
 */

(function() {

    var canvas, ctx, code, point, style, drag = null, dPoint;

    // define initial points
    function Init(quadratic) {

        point = {
            p1: { x:100, y:250 },
            p2: { x:400, y:250 }
        };

        if (quadratic) {
            point.cp1 = { x: 250, y: 100 };
        }
        else {
            point.cp1 = { x: 150, y: 100 };
            point.cp2 = { x: 350, y: 100 };
        }

        // default styles
        style = {
            //#333
            curve:  { width: 2, color: "#C11" },
            cpline: { width: 1, color: "#C11" },
            point: { radius: 10, width: 2, color: "#900", fill: "rgba(200,200,200,0.5)", arc1: 0, arc2: 2 * Math.PI }
        }

        // line style defaults
        ctx.lineCap = "round";
        ctx.lineJoin = "round";

        // event handlers
        canvas.onmousedown = DragStart;
        canvas.onmousemove = Dragging;
        canvas.onmouseup = canvas.onmouseout = DragEnd;

        DrawCanvas();
    }

    function controlLine(offset) {
        // curve
        ctx.lineWidth = style.curve.width;
        ctx.strokeStyle = style.curve.color;
        ctx.beginPath();
        ctx.moveTo(point.p1.x+offset, point.p1.y+offset);
        ctx.bezierCurveTo(point.cp1.x+offset, point.cp1.y+offset, point.cp2.x+offset, point.cp2.y+offset, point.p2.x+offset, point.p2.y+offset);
        ctx.stroke();
    }

    function controlPoints(/*hidden*/) {
        // control point tethers
        ctx.lineWidth = style.cpline.width;
        ctx.strokeStyle = style.cpline.color;
        ctx.beginPath();
        ctx.moveTo(point.p1.x, point.p1.y);
        ctx.lineTo(point.cp1.x, point.cp1.y);
            ctx.moveTo(point.p2.x, point.p2.y);
            ctx.lineTo(point.cp2.x, point.cp2.y);
        ctx.stroke();

        // control points
        for (var p in point) {
            ctx.lineWidth = style.point.width;
            ctx.strokeStyle = style.point.color;
            ctx.fillStyle = style.point.fill;
            ctx.beginPath();
            ctx.arc(point[p].x, point[p].y, style.point.radius, style.point.arc1, style.point.arc2, true);
            ctx.fill();
            ctx.stroke();
        }
    } 


    // draw canvas
    function DrawCanvas() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        controlLine(-10);
        controlLine(+10);
        controlPoints();
    }

    // start dragging
    function DragStart(e) {
        e = MousePos(e);
        var dx, dy;
        for (var p in point) {
            dx = point[p].x - e.x;
            dy = point[p].y - e.y;
            if ((dx * dx) + (dy * dy) < style.point.radius * style.point.radius) {
                drag = p;
                dPoint = e;
                canvas.style.cursor = "move";
                return;
            }
        }
    }


    // dragging
    function Dragging(e) {
        if (drag) {
            e = MousePos(e);
            point[drag].x += e.x - dPoint.x;
            point[drag].y += e.y - dPoint.y;
            dPoint = e;
            DrawCanvas();
        }
    }


    // end dragging
    function DragEnd(e) {
        drag = null;
        canvas.style.cursor = "default";
        DrawCanvas();
    }


    // event parser
    function MousePos(event) {
        event = (event ? event : window.event);
        return {
            x: event.pageX - canvas.offsetLeft,
            y: event.pageY - canvas.offsetTop
        }
    }


    // start
    canvas = document.getElementById("canvas");
    code = document.getElementById("code");
    if (canvas.getContext) {
        ctx = canvas.getContext("2d");
        Init(canvas.className == "quadratic");
    }

})();

Безье.html

<!--
   bezier.html

   Copyright 2012 DT <dtyree@inkcogito>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA.


-->

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

 <head>
    <title>untitled</title>
    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
    <meta name="generator" content="Geany 0.21" />
     <meta charset="UTF-8" />
     <title>B&#233;zier Example</title>
 </head>

 <link rel="stylesheet" type="text/css" media="all" href="styles.css" />

 <body>

    <h1>Canvas B&#233;zier Curve Example</h1>

    <canvas id="canvas" height="500" width="500" class="bezier"></canvas>
    <pre id="code">code</pre>

    <p>This demonstration shows how parallel b&#233;zier curves can be drawn on a canvas element. Drag the line ends or the control points to change the curve.</p>

    <script type="text/javascript" src="bezier.js"></script>

 </body>

 </html>

styles.css
/* CSS */ body { font-family: arial, helvetica, sans-serif; размер шрифта: 85%; поля: 10px 15px; цвет: #333; цвет фона: #ddd; }

 h1
 {
    font-size: 1.6em;
    font-weight: normal;
    margin: 0 0 0.3em 0;
 }

 h2
 {
    font-size: 1.4em;
    font-weight: normal;
    margin: 1.5em 0 0 0;
 }

 p
 {
    margin: 1em 0;
 }

 #canvas
 {
    display: inline;
    float: left;
    margin: 0 10px 10px 0;
    background-color: #fff;
 }
person Dtyree    schedule 23.05.2012
comment
Боюсь, это не сработает: imageshack.us/photo/my -images/23/parallelm.png Две линии не находятся на одинаковом расстоянии от центра, но могут сблизиться и даже пересечься. - person Thomas Ahle; 24.05.2012
comment
Да, как я уже говорил, это не капля раствора; Это только начало решения. Необходимо добавить функцию масштабирования для настройки смещения контрольных точек по отношению друг к другу. Если у меня будет время, я все улажу. По сути, это 2,5D-решение для 2D-плоскости. - person Dtyree; 24.05.2012
comment
Смотрите это для объяснения... processingjs.nihongoresources.com/bezierinfo/#offsets дополнительно в библиотеке d3.js есть хорошая функция для масштабирования смещения. - person Dtyree; 24.05.2012
comment
Это не сложная проблема, но масштабирование смещения трудоемко для написания кода, около 10 часов с нуля. Я делал это пару раз на разных языках. Программирование графики должно заставить вас ценить математику. - person Dtyree; 25.05.2012
comment
@Dtyree: Где в исходном коде d3.js находится их код масштабирования смещения? (Мне нужно реализовать это для холста Безье, так как я пытаюсь учесть переменную толщину) Спасибо. - person aehlke; 13.06.2012