7

我正在尝试在 AS3(纯而不是 Flex)中制作一个“画笔”工具,它可以模拟笔迹,使笔触平滑而不是拐角。然后,必须将迹线简化为可以拖动和变形的三次贝塞尔曲线,从而影响先前绘制的路径(如插图画家的钢笔工具)。

我正在跟踪鼠标移动以获取一组点来绘制路径。据我所知,我需要使用这组点做一个 B 样条路径。然后我应该将它简化为三次贝塞尔曲线(将“钢笔工具”功能添加到路径中)。

我已经开发了钢笔工具,使用一种将三次贝塞尔曲线减少为二次贝塞尔曲线的算法(然后使用 Flash curveTo 函数)。但我不知道如何创建 B 样条(或其他简化),然后将其简化为贝塞尔曲线。

你知道有什么方法可以做到这一点吗?

4

4 回答 4

6

jhotdraw是一个用于绘图的Java 开源项目。它将手绘图转换为三次贝塞尔曲线。源代码可用 - 下载并翻译。不要对项目的规模感到害怕:您只需要几个类,即:

org.jhotdraw.geom.Bezier
org.jhotdraw.geom.BezierPath
org.jhotdraw.geom.Geom

翻译时首先将所有集合声明更改为数组(如果您仅针对 FP10 用户,请使用向量)。我有一些您可能会在转换中发现有用的正则表达式 - 如果您愿意,我可以发布它们。


这是您可能会发现有用的正则表达式列表。在每一对中,将第一个粘贴到搜索文本区域,第二个粘贴到替换区域,选中正则表达式复选框并使用查找和替换按钮。不要使用 Replace All - 这些都不能保证万无一失

将所有int/double name声明替换为var name:Number

\b(double|int)\s+(\w+)\b

var $2:Number

将所有Point2D.Double name声明替换为var name:Point

\bPoint2D\.Double\s+(\w+)\b

var $1:Point

int/double name将函数签名中的所有声明替换为name:Number

\(([^)]*)\b(?:double|int)\s+(\w+)\b([^)]*?)\)

($1$2:Number$3)

Point2D.Double name将函数签名中的所有声明替换为name:Point

\(([^)]*)\b(?:Point2D\.Double)\s+(\w+)\b([^)]*?)\) 

($1$2:Point$3)

在更改方法签名之前,请确保所有方法都是静态的:

(public|private)\s+(?!static)

将方法签名替换为 AS 格式

(public|private)\s+static\s+(\w+)\s+(\w+)\s*\(([^)]*)\)

$1 static function $3($4):$2

Replace ArrayList.get(index) with array[index] //Warning: failed for list.get(list.size() - 1)

(\w+)\.get\(([^)]+)\)

$1[$2]

//avoid the () failure 

(\w+)\.get\(([^)]*(?:\([^)]*\))[^)]*)\)

$1[$2]

替换ArrayList.set(index, element)array[index] = element //Warning: failed for list.set(i, list.size())

(\w+)\.set\(([^,]+)\s*,\s*([^)]+)\)

$1[$2] = $3


/*the above regex successfully made the following replacement*/

cleaned.set(cleaned.size() - 1, digitizedPoints[digitizedPoints.size() - 1])

cleaned[cleaned.size() - 1] = digitizedPoints[digitizedPoints.size() - 1]

替换arraylist.add(object)array.push(object)

//would fail if object contains ')'
//add(index, object) should be done with splice

(\w+)\.add\(([^)]+)\)

$1.push($2)

//too many failures - fail safe version - 
//still fails for nested parenthesis  list.add(new Point(a.first(), a.last())) 
//- only three such cases - the effort to match parenthesis wouldn't be worth it
//works for list.add(new Point(3, 4)) - there were many similar cases

(\w+)\.add\(([^)]*(?:\([^)]*\))[^)]*)\)

$1.push($2)

将方法签名替换为 AS 格式(非静态方法)

(public|private)\s+(?!function)(\w+)\s+(\w+)\s*\(([^)]*)\)

$1 function $3($4):$2

int/double/point/boolean name将函数签名中的所有声明替换为name:type

\(([^)]*)\b(\w+)\s+(\w+)\b([^)]*?)\)

($1$3:$2$4)

用 = 到 AS 格式替换它自己的行中的所有变量声明

^(\s+)(\w+)\s+(\w+)\s*=\s*(.+?)\s*;(\s*)$

$1var $3:$2 = $4;$5

改变牙套的位置。

^(\t)(\s*)([^\n]+)\{\s*(\n)\s+

$1$2$3$4$1$2{$4$1$2

变成} else_} \n else

^([ \t]+)}[ \t]*else\b([^\n]*)(\n)

$1}$3$1else$2$3

将单行中的 4 个变量声明替换为不同行中的 AS

^(\t+)(\w+)\s+(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*,\s*(\w+)\s*;[ \t]*(\n)

$1var $3:$2;$7$1var $4:$2;$7$1var $5:$2;$7$1var $6:$2;$7

替换数组声明

^(\s+)\w+\[\]\s*(\w+)\b

$1 var $2:Array

Remove () 强制转换 - AS 编译器不喜欢它们

(?:\(\w+\)\s*)([^ ,*+;/)><=\-])

$1

将 max 等替换为 Math.max - AS 没有静态导入

(?<!Math\.)\b(max|min|abs|sqrt|PI|cos|sin|atan2)\(

Math.$1(
于 2009-11-19T17:11:30.953 回答
1

我曾经使用过这个功能。


    public function multicurve(g: Graphics, args: Array, closed: Boolean): void {           
            var mid: Array = args.slice();  //make dublicate
            var i: uint;
            var point: Point;
            var nextPoint: Point;
            var numPoints: uint = mid.length;

            if (numPoints == 2) {
                g.moveTo(mid[0].x, mid[0].y);
                g.lineTo(mid[1].x, mid[1].y);
                return;
            }

            var Xpoint: Array = new Array();
            var Ypoint: Array = new Array();
            for (i = 1; i < numPoints - 2; i++) {
                point = mid[i];
                nextPoint = mid[i+1];
                Xpoint[i] = 0.5*(nextPoint.x + point.x);
                Ypoint[i] = 0.5*(nextPoint.y + point.y);
            }
            if (closed) {
                Xpoint[0] = 0.5*(mid[1].x + mid[0].x);
                Ypoint[0] = 0.5*(mid[1].y + mid[0].y);
                Xpoint[i] = 0.5*(mid[i+1].x + mid[i].x);
                Ypoint[i] = 0.5*(mid[i+1].y + mid[i].y);
                Xpoint[i+1] = 0.5*(mid[i+1].x + mid[0].x);
                Ypoint[i+1] = 0.5*(mid[i+1].y + mid[0].y);
                mid.push(new Point(mid[0].x, mid[0].y));
                Xpoint[i+2] = Xpoint[0];
                Ypoint[i+2] = Ypoint[0];
            } else {
                Xpoint[0] = mid[0].x;
                Ypoint[0] = mid[0].y;
                Xpoint[i] = mid[i+1].x;
                Ypoint[i] = mid[i+1].y;
                mid.pop();
                numPoints--;
            }
            g.moveTo(Xpoint[0], Ypoint[0]);
            for (i = 1; i < numPoints; i++) {
                point = mid[i];
                g.curveTo(point.x, point.y, Xpoint[i], Ypoint[i]);
            }
            if (closed) {
                g.curveTo(mid[0].x, mid[0].y, Xpoint[i], Ypoint[i]);
            }
        }


于 2009-11-20T16:49:31.687 回答
0

不确定您是否特别需要贝塞尔曲线,但这个 catmull-rom 样条工具非常棒: http ://www.motiondraw.com/md/as_samples/t/CatmullRomSpline/tween.html

于 2009-12-01T19:13:21.140 回答
0

ac 库中有一个算法可以满足您的要求: http ://tog.acm.org/resources/GraphicsGems/gems/FitCurves.c

这是一个相当复杂的算法,它通过将许多点的列表转换为一些紧密拟合的贝塞尔曲线的列表来简化您的几何图形,基本上将涂鸦变成非常平滑的曲线。它具有可调节的松弛量,并通过在一定的松弛范围内找到适合您的点集的最少贝塞尔曲线来工作。因此,您将算法的 slack 设置得越高,您的写作就越流畅(但可能不太准确)。

于 2011-01-27T03:26:59.830 回答