我需要与GraphicsPath.Widen()
.Net中的方法相反:
public GraphicsPath Widen()
该Widen()
方法不接受负参数,所以我需要一个Inset
方法的等价物:
public GraphicsPath Inset()
您可以在开源 Inkscape 应用程序 (www.Inkscape.org) 中执行此操作,方法是转到菜单并选择“Path / Inset”(Inset 数量存储在 Inkscape 属性对话框中)。由于 Inkscape 是开源的,应该可以在 C#.Net 中执行此操作,但我无法终生遵循 Inkscape C++ 源代码(我只需要这个功能,所以我不能证明学习 C++来完成这个)。
基本上,我需要一个带有此签名的 GraphicsPath 扩展方法:
public static GraphicsPath Inset(this GraphicsPath original, float amount)
{
//implementation
}
正如签名所述,它将采用一个GraphicsPath
对象和.Inset()
路径通过的数量......就像今天的 Inkscape 一样。如果它简化了任何事情,那么有问题的 GraphicsPaths 都是从该.PolyBezier
方法创建的(仅此而已),因此无需考虑矩形、椭圆或任何其他形状,除非您为了完整性而这样做。
不幸的是,我没有使用 C++ 代码的经验,所以我几乎不可能遵循 Inkscape 中包含的 C++ 逻辑。
.
[编辑:] 根据要求,这里是“MakeOffset”Inkscape 代码。第二个参数(double dec)对于 Inset 为负数,该参数的绝对值是引入形状的量。
我知道这里有很多依赖项。如果您需要查看更多 Inkscape 源文件,请访问:http: //sourceforge.net/projects/inkscape/files/inkscape/0.48/
int
Shape::MakeOffset (Shape * a, double dec, JoinType join, double miter, bool do_profile, double cx, double cy, double radius, Geom::Matrix *i2doc)
{
Reset (0, 0);
MakeBackData(a->_has_back_data);
bool done_something = false;
if (dec == 0)
{
_pts = a->_pts;
if (numberOfPoints() > maxPt)
{
maxPt = numberOfPoints();
if (_has_points_data) {
pData.resize(maxPt);
_point_data_initialised = false;
_bbox_up_to_date = false;
}
}
_aretes = a->_aretes;
if (numberOfEdges() > maxAr)
{
maxAr = numberOfEdges();
if (_has_edges_data)
eData.resize(maxAr);
if (_has_sweep_src_data)
swsData.resize(maxAr);
if (_has_sweep_dest_data)
swdData.resize(maxAr);
if (_has_raster_data)
swrData.resize(maxAr);
if (_has_back_data)
ebData.resize(maxAr);
}
return 0;
}
if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1 || a->type != shape_polygon)
return shape_input_err;
a->SortEdges ();
a->MakeSweepDestData (true);
a->MakeSweepSrcData (true);
for (int i = 0; i < a->numberOfEdges(); i++)
{
// int stP=a->swsData[i].stPt/*,enP=a->swsData[i].enPt*/;
int stB = -1, enB = -1;
if (dec > 0)
{
stB = a->CycleNextAt (a->getEdge(i).st, i);
enB = a->CyclePrevAt (a->getEdge(i).en, i);
}
else
{
stB = a->CyclePrevAt (a->getEdge(i).st, i);
enB = a->CycleNextAt (a->getEdge(i).en, i);
}
Geom::Point stD, seD, enD;
double stL, seL, enL;
stD = a->getEdge(stB).dx;
seD = a->getEdge(i).dx;
enD = a->getEdge(enB).dx;
stL = sqrt (dot(stD,stD));
seL = sqrt (dot(seD,seD));
enL = sqrt (dot(enD,enD));
MiscNormalize (stD);
MiscNormalize (enD);
MiscNormalize (seD);
Geom::Point ptP;
int stNo, enNo;
ptP = a->getPoint(a->getEdge(i).st).x;
double this_dec;
if (do_profile && i2doc) {
double alpha = 1;
double x = (Geom::L2(ptP * (*i2doc) - Geom::Point(cx,cy))/radius);
if (x > 1) {
this_dec = 0;
} else if (x <= 0) {
this_dec = dec;
} else {
this_dec = dec * (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
}
} else {
this_dec = dec;
}
if (this_dec != 0)
done_something = true;
int usePathID=-1;
int usePieceID=0;
double useT=0.0;
if ( a->_has_back_data ) {
if ( a->ebData[i].pathID >= 0 && a->ebData[stB].pathID == a->ebData[i].pathID && a->ebData[stB].pieceID == a->ebData[i].pieceID
&& a->ebData[stB].tEn == a->ebData[i].tSt ) {
usePathID=a->ebData[i].pathID;
usePieceID=a->ebData[i].pieceID;
useT=a->ebData[i].tSt;
} else {
usePathID=a->ebData[i].pathID;
usePieceID=0;
useT=0;
}
}
if (dec > 0)
{
Path::DoRightJoin (this, this_dec, join, ptP, stD, seD, miter, stL, seL,
stNo, enNo,usePathID,usePieceID,useT);
a->swsData[i].stPt = enNo;
a->swsData[stB].enPt = stNo;
}
else
{
Path::DoLeftJoin (this, -this_dec, join, ptP, stD, seD, miter, stL, seL,
stNo, enNo,usePathID,usePieceID,useT);
a->swsData[i].stPt = enNo;
a->swsData[stB].enPt = stNo;
}
}
if (dec < 0)
{
for (int i = 0; i < numberOfEdges(); i++)
Inverse (i);
}
if ( _has_back_data ) {
for (int i = 0; i < a->numberOfEdges(); i++)
{
int nEd=AddEdge (a->swsData[i].stPt, a->swsData[i].enPt);
ebData[nEd]=a->ebData[i];
}
} else {
for (int i = 0; i < a->numberOfEdges(); i++)
{
AddEdge (a->swsData[i].stPt, a->swsData[i].enPt);
}
}
a->MakeSweepSrcData (false);
a->MakeSweepDestData (false);
return (done_something? 0 : shape_nothing_to_do);
}
.
[编辑]
@Simon Mourier - 了不起的工作。代码甚至干净易读!干得好,先生。不过,我确实有几个问题要问你。
首先,金额的正数代表什么?我在想,对于 Offset 方法,正数将是“开始”,负数将是“插入”,但您的示例似乎相反。
其次,我做了一些基本测试(只是扩展了你的样本),发现了一些奇怪的地方。
以下是当偏移量增加时,cool 中的“l”会发生什么(对于这样一个简单的字母,它肯定会引起问题!)。
...以及重现该代码的代码:
private void Form1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath path = new GraphicsPath();
path.AddString("cool", new FontFamily("Arial"), 0, 200, new PointF(), StringFormat.GenericDefault);
GraphicsPath offset1 = path.Offset(32);
e.Graphics.DrawPath(new Pen(Color.Black, 1), path);
e.Graphics.DrawPath(new Pen(Color.Red, 1), offset1);
}
最后,有点不同。这是 Wingdings 中的“S”字符(看起来像一滴眼泪):
这是代码:
private void Form1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath path = new GraphicsPath();
path.AddString("S", new FontFamily("Wingdings"), 0, 200, new PointF(), StringFormat.GenericDefault);
GraphicsPath offset1 = path.Offset(20);
e.Graphics.DrawPath(new Pen(Color.Black, 1), path);
e.Graphics.DrawPath(new Pen(Color.Red, 1), offset1);
}
天啊,离得这么近,看得我想哭。但是,它仍然不起作用。
我认为解决这个问题的方法是查看插入向量何时相交,并停止插入超过该点。如果插入量太大(或路径太小)以至于什么都没有,则路径应该消失(变为空),而不是自身反转并重新扩展。
同样,我不会以任何方式敲击您所做的事情,但我想知道您是否知道这些示例可能会发生什么。
(PS - 我添加了 'this' 关键字以使其成为扩展方法,因此您可能需要使用方法(参数)表示法调用代码才能运行这些示例)
.
@RAN Ran 通过重新使用 GraphicsPath 本机方法得出了类似的输出。伙计,这很难。他们俩都那么亲近。
这是两个示例的屏幕截图,使用了 Wingdings 中的字符“S”:
@Simon 在左边,@Ran 在右边。
这是在 Inkscape 中进行“Inset”之后的相同泪滴“S”字符。插图很干净:
顺便说一下,这是@Ran 的测试代码:
private void Form1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath path = new GraphicsPath();
path.AddString("S", new FontFamily("Wingdings"), 0, 200, new PointF(), StringFormat.GenericDefault);
e.Graphics.DrawPath(new Pen(Color.Black, 1), path);
GraphicsPath offset1 = path.Shrink(20);
e.Graphics.DrawPath(new Pen(Color.Red, 1), offset1);
}