本文介绍不依赖贝塞尔曲线,如何绘制一条平滑曲线,用于解决无贝塞尔控制点的情况下绘制曲线、但数据点不在贝塞尔曲线的场景。
在上一家公司我做过一个平滑曲线编辑工具,用于轮椅调整加减速曲线。基于几个用户可控制的点,生成一条平滑的曲线,控制点需要保持在曲线上。
今天和小伙伴沟通,白板以自定义形状绘制笔迹,也可以使用到这个数据点模拟的技术,我回顾总结下
贝塞尔平滑曲线
我们先讲贝塞尔曲线GDI+ 中的贝塞尔自由绘制曲线 - Windows Forms .NET Framework | Microsoft Learn。一般情况我们绘制平滑曲线,直接以贝塞尔曲线API将多个点作为参数,直接进行绘制。这种情况下API会自动将第一个点作为控制点,得到贝塞尔曲线,比如下面生成一条平滑Geometry:
1 var geometryTest = new StreamGeometry();2 using(var ctx = geometryTest.Open())3 {4 ctx.BeginFigure(_points[0], true, false);5 if(keyPoints.Count % 2 == 0)6 {7 //绘制二阶贝塞尔函数,需要保证为偶数点8 ctx.PolyQuadraticBezierTo(keyPoints, true, true);9 } 10 else 11 { 12 //绘制二阶贝塞尔函数,需要保证为偶数点 13 keyPoints.Insert(0, keyPoints[0]); 14 ctx.PolyQuadraticBezierTo(keyPoints, true, true); 15 } 16 }
这里的PolyQuadraticBezierTo函数,塞点集列表进去并设置平滑参数isSmoothJoin=true
1 public abstract void PolyQuadraticBezierTo( 2 IList<Point> points, 3 bool isStroked, 4 bool isSmoothJoin); 5 6 public abstract void PolyBezierTo(IList<Point> points, bool isStroked, bool isSmoothJoin);
官网有介绍,列表中第一个点作为控制点:StreamGeometryContext.PolyQuadraticBezierTo 方法 (System.Windows.Media) |Microsoft 学习
上面是自动设置控制点,这类实现方案会有一个问题:数据点最终可能不在曲线上
基于贝塞尔曲线,我们也可以计算控制点。但计算控制点,也是同样无法保证原始数据点会在拟合后的曲线上。
模拟平滑曲线
以现有数据点,如果直接相连肯定只会生成多个折线。如果我们添加多个点,可以模拟一条类似曲线路径的多边形近似点集,与Geometry下的FlattenedPathGeometry有点类似。
方案一,可以使用MathNet.Numerics生成一条X方向的N阶曲线,然后输入X坐标输出Y坐标,得到曲线上的点。 MathNet.Numerics可以参考 .NET 白板书写加速-曲线拟合预测 - 唐宋元明清2188 - 博客园。但这方案会生成无数点,曲线绘制性能无法得到保证。所以添加这些曲线路径的点,如何以最小的点集实现?可以对相邻点,对向量角度变化以及相邻间距设置一个最小阈值,最终得到符合的点集
方案二,用我之前实现方案,根据最简多项式代码算出近似样条曲线点集。原理同MathNet.Numerics里的Polynomial函数,下面是部分代码:
1 private const double Tolerance = 0.5;2 3 /// <summary>4 /// 获取拟合后的点集5 /// </summary>6 /// <param name="points"></param>7 /// <returns></returns>8 public static List<Point> GetFittingLinePoints(List<Point> points)9 { 10 var orderedPoints = (from pt in points orderby pt.X select pt).ToList(); 11 var secondDerivatives = SecondDerivativeHelper.GetSecondDerivatives(orderedPoints); 12 List<Point> polyLinePoints = PointFakeApproximationHelper.GetSplinePolyLineApproximation(orderedPoints, secondDerivatives, Tolerance); 13 return polyLinePoints; 14 }
jselt.b2b.h3cw.com
sgshq.b2b.h3cw.com
iodze.b2b.h3cw.com
ssttg.b2b.h3cw.com
lbaox.b2b.h3cw.com
cbjox.b2b.h3cw.com
bwbkm.b2b.h3cw.com
eiuuj.b2b.h3cw.com
soabq.b2b.h3cw.com
qpkex.b2b.h3cw.com
lrmoh.b2b.h3cw.com
lxtya.b2b.h3cw.com
xgoun.b2b.h3cw.com
yrnmo.b2b.h3cw.com
dfhxt.b2b.h3cw.com
ziujp.b2b.h3cw.com
yhtiv.b2b.h3cw.com
sihwr.b2b.h3cw.com
sqnkp.b2b.h3cw.com
mibqp.b2b.h3cw.com
nmbgi.b2b.h3cw.com
frmuz.b2b.h3cw.com
htwix.b2b.h3cw.com
jrxsz.b2b.h3cw.com
kjlnm.b2b.h3cw.com
pbcvr.b2b.h3cw.com
sotir.b2b.h3cw.com
eqsbt.b2b.h3cw.com
gikcr.b2b.h3cw.com
ikfdl.b2b.h3cw.com
thdwn.b2b.h3cw.com
bopwy.b2b.h3cw.com
azbmb.b2b.h3cw.com
zsegp.b2b.h3cw.com
amadi.b2b.h3cw.com
ytchj.b2b.h3cw.com
lxdzk.b2b.h3cw.com
dsntv.b2b.h3cw.com
qafue.b2b.h3cw.com
iujox.b2b.h3cw.com
mymbn.b2b.h3cw.com
rwlez.b2b.h3cw.com
vcrwf.b2b.h3cw.com
mbqcx.b2b.h3cw.com
lnpup.b2b.h3cw.com
inwle.b2b.h3cw.com
ljhxo.b2b.h3cw.com
bdyyk.b2b.h3cw.com
ciqie.b2b.h3cw.com
mbnpe.b2b.h3cw.com
irfhz.b2b.h3cw.com
ibwld.b2b.h3cw.com
jidsa.b2b.h3cw.com
apacl.b2b.h3cw.com
vpydj.b2b.h3cw.com
dgoav.b2b.h3cw.com
gwexz.b2b.h3cw.com
okfem.b2b.h3cw.com
zcirk.b2b.h3cw.com
bxpkk.b2b.h3cw.com
zydmy.b2b.h3cw.com
umiqe.b2b.h3cw.com
ttirc.b2b.h3cw.com
lhfot.b2b.h3cw.com
vveql.b2b.h3cw.com
egihq.b2b.h3cw.com
baiew.b2b.h3cw.com
xzrav.b2b.h3cw.com
cwcxz.b2b.h3cw.com
gxgbu.b2b.h3cw.com
tpkcx.b2b.h3cw.com
powsr.b2b.h3cw.com
cbqyx.b2b.h3cw.com
tihuj.b2b.h3cw.com
qthtj.b2b.h3cw.com
xmbac.b2b.h3cw.com
qprzb.b2b.h3cw.com
pnjid.b2b.h3cw.com
mytsq.b2b.h3cw.com
ngvod.b2b.h3cw.com
nzvaj.b2b.h3cw.com
bcdcs.b2b.h3cw.com
ngonn.b2b.h3cw.com
gcxce.b2b.h3cw.com
xgohj.b2b.h3cw.com
kceqm.b2b.h3cw.com
qglet.b2b.h3cw.com
baqbd.b2b.h3cw.com
wlemm.b2b.h3cw.com
fbbwv.b2b.h3cw.com
pytqz.b2b.h3cw.com
jqyrn.b2b.h3cw.com
giqpy.b2b.h3cw.com
vhapo.b2b.h3cw.com
jvrgd.b2b.h3cw.com
rxzly.b2b.h3cw.com
fyaiz.b2b.h3cw.com
yxzvn.b2b.h3cw.com
yusoa.b2b.h3cw.com
ionix.b2b.h3cw.com
lkzio.b2b.h3cw.com
glnjg.b2b.h3cw.com
uwidm.b2b.h3cw.com
sbgik.b2b.h3cw.com
rdxzi.b2b.h3cw.com
ucvds.b2b.h3cw.com
jleml.b2b.h3cw.com
jfntt.b2b.h3cw.com
tzfbo.b2b.h3cw.com
gfawg.b2b.h3cw.com
yezvz.b2b.h3cw.com
vudfx.b2b.h3cw.com
yyteu.b2b.h3cw.com
hgvxw.b2b.h3cw.com
qfunp.b2b.h3cw.com
njews.b2b.h3cw.com
oedmr.b2b.h3cw.com
slgph.b2b.h3cw.com
zihqv.b2b.h3cw.com
mbqjc.b2b.h3cw.com
ggoqh.b2b.h3cw.com
oujyw.b2b.h3cw.com
zluzv.b2b.h3cw.com
qdmut.b2b.h3cw.com
xmetm.b2b.h3cw.com
ntubu.b2b.h3cw.com
ehzhw.b2b.h3cw.com
dfyag.b2b.h3cw.com
ngodl.b2b.h3cw.com
bxyee.b2b.h3cw.com
edlnm.b2b.h3cw.com
vhcyk.b2b.h3cw.com
xasia.b2b.h3cw.com
njkkf.b2b.h3cw.com
dwyuz.b2b.h3cw.com
wcqks.b2b.h3cw.com
edssy.b2b.h3cw.com
jvkmu.b2b.h3cw.com
lprdm.b2b.h3cw.com
ixwrn.b2b.h3cw.com
jjlgs.b2b.h3cw.com
hnpxe.b2b.h3cw.com
kpyln.b2b.h3cw.com
aprhj.b2b.h3cw.com
pocck.b2b.h3cw.com
onwvq.b2b.h3cw.com
zyaph.b2b.h3cw.com
knlac.b2b.h3cw.com
vchwi.b2b.h3cw.com
qqlkt.b2b.h3cw.com
zfbdu.b2b.h3cw.com
zpkwy.b2b.h3cw.com
yznts.b2b.h3cw.com
tygmv.b2b.h3cw.com
ujbww.b2b.h3cw.com
ibcld.b2b.h3cw.com
qmkmi.b2b.h3cw.com
viixj.b2b.h3cw.com
puuph.b2b.h3cw.com
zfucu.b2b.h3cw.com
exnsu.b2b.h3cw.com
dsozp.b2b.h3cw.com
fuwib.b2b.h3cw.com
uxfet.b2b.h3cw.com
hodcr.b2b.h3cw.com
atzba.b2b.h3cw.com
ngbqj.b2b.h3cw.com
ktunp.b2b.h3cw.com
ydwrn.b2b.h3cw.com
iuwfq.b2b.h3cw.com
qfhgi.b2b.h3cw.com
prxsr.b2b.h3cw.com
kzrmi.b2b.h3cw.com
edmlt.b2b.h3cw.com
oqfet.b2b.h3cw.com
pokwe.b2b.h3cw.com
rxyra.b2b.h3cw.com
vbujr.b2b.h3cw.com
iivuj.b2b.h3cw.com
srgfu.b2b.h3cw.com
srtmh.b2b.h3cw.com
zmnmi.b2b.h3cw.com
cfazu.b2b.h3cw.com
wgrkx.b2b.h3cw.com
qsfnz.b2b.h3cw.com
plnfb.b2b.h3cw.com
cbkfl.b2b.h3cw.com
scujp.b2b.h3cw.com
ciidj.b2b.h3cw.com
tvqil.b2b.h3cw.com
hsajv.b2b.h3cw.com
kmrgt.b2b.h3cw.com
zravj.b2b.h3cw.com
wveqs.b2b.h3cw.com
iudcr.b2b.h3cw.com
rqytm.b2b.h3cw.com
qpksf.b2b.h3cw.com
eaohp.b2b.h3cw.com
oksrz.b2b.h3cw.com
gvkpy.b2b.h3cw.com
myame.b2b.h3cw.com
xpemf.b2b.h3cw.com
exmou.b2b.h3cw.com
xgrnj.b2b.h3cw.com
kglsa.b2b.h3cw.com
coqfr.b2b.h3cw.com
iavrg.b2b.h3cw.com
xckmy.b2b.h3cw.com
clmfu.b2b.h3cw.com
prmyu.b2b.h3cw.com
rxzha.b2b.h3cw.com
onpew.b2b.h3cw.com
cokzy.b2b.h3cw.com
jvvrm.b2b.h3cw.com
bjvxf.b2b.h3cw.com
odser.b2b.h3cw.com
ehiop.b2b.h3cw.com
cewrt.b2b.h3cw.com
mogix.b2b.h3cw.com
ralhm.b2b.h3cw.com
dpfqs.b2b.h3cw.com
ujljs.b2b.h3cw.com
bhqve.b2b.h3cw.com
rjytg.b2b.h3cw.com
gzbqi.b2b.h3cw.com
bgxbf.b2b.h3cw.com
odced.b2b.h3cw.com
yotbw.b2b.h3cw.com
fxzla.b2b.h3cw.com
djxeg.b2b.h3cw.com
获取俩点之间点集:
1 /// <summary>2 /// 用折线逼近三次多项式并给出公差。3 /// </summary>4 /// <param name="leftPoint">三次多项式左点</param>5 /// <param name="rightPoint">三次多项式右点</param>6 /// <param name="secondDerivativeLeft">左点三次多项式二阶导数.</param>7 /// <param name="secondDerivativeRight">右端三次多项式二阶导数.</param>8 /// <param name="tolerance">公差,即样条到近似折线的最大距离。</param>9 /// <returns>在给定公差的情况下逼近三次多项式的多边形点的列表。</returns> 10 private static Collection<Point> GetApproximation(Point leftPoint, Point rightPoint, double secondDerivativeLeft, double secondDerivativeRight, double tolerance) 11 { 12 // 左右俩点的X、Y轴值 13 double leftPointX = leftPoint.X, rightPointX = rightPoint.X; 14 double leftPointY = leftPoint.Y, rightPointY = rightPoint.Y; 15 // 次区间多项式系数 16 double a = (secondDerivativeRight - secondDerivativeLeft) / (6 * (rightPointX - leftPointX)); 17 double b = (secondDerivativeLeft - 6 * a * leftPointX) / 2; 18 double c = (rightPointY - rightPointX * rightPointX * (a * rightPointX + b) - leftPointY + leftPointX * leftPointX * (a * leftPointX + b)) / (rightPointX - leftPointX); 19 double d = leftPointY - leftPointX * (leftPointX * (a * leftPointX + b) + c); 20 21 //如果a的值为0,则给a赋值double类型的最小正数 22 if(a == 0) 23 a = double.Epsilon; 24 25 //通过多项式与拆线的逼近,获取多边形点的列表 26 Collection<Point> points = CubicPolynomialPolylineApproximation.Approximate(new Polynomial(new double[] { d, c, b, a }), leftPointX, rightPointX, tolerance); 27 return points; 28 }
效果如下图,左侧是原数据点集(绿色),右则截图是模拟后的点集显示(红色):
把这些新增点与原数据点,用直接连接起来,就是一条比较平滑的曲线了。同时原数据点也在拟合的曲线上。另外,如果还需要优化下这些线段的平滑,可以使用贝塞尔曲线(见上面贝塞尔的实现)替换直线连接,已经加了很多密集点,绿点不会脱离曲线的。
Github仓库代码 GitHub - kybs00/CurveLineEditDemo: 平滑曲线模拟及编辑Demo
kwlxg.b2b.h3cw.com
rqyts.b2b.h3cw.com
nnbdp.b2b.h3cw.com
lutvb.b2b.h3cw.com
bhhms.b2b.h3cw.com
gmonw.b2b.h3cw.com
njvrj.b2b.h3cw.com
njpxk.b2b.h3cw.com
upxoq.b2b.h3cw.com
nprpl.b2b.h3cw.com
wbkje.b2b.h3cw.com
wsyse.b2b.h3cw.com
rixqy.b2b.h3cw.com
ftasv.b2b.h3cw.com
bnhac.b2b.h3cw.com
fujbk.b2b.h3cw.com
vniyd.b2b.h3cw.com
jzrtf.b2b.h3cw.com
ccxem.b2b.h3cw.com
fetlu.b2b.h3cw.com
jmeav.b2b.h3cw.com
ydlug.b2b.h3cw.com
kwewf.b2b.h3cw.com
giqpe.b2b.h3cw.com
cbdsa.b2b.h3cw.com
kxxme.b2b.h3cw.com
nnmil.b2b.h3cw.com
ixjlg.b2b.h3cw.com
cyoyk.b2b.h3cw.com
nupir.b2b.h3cw.com
yetfn.b2b.h3cw.com
zvotc.b2b.h3cw.com
gzbdw.b2b.h3cw.com
nzura.b2b.h3cw.com
qpcej.b2b.h3cw.com
eydzy.b2b.h3cw.com
obqvr.b2b.h3cw.com
lenfy.b2b.h3cw.com
bqcjf.b2b.h3cw.com
udgck.b2b.h3cw.com
zstco.b2b.h3cw.com
vxedl.b2b.h3cw.com
ymoti.b2b.h3cw.com
gsoqo.b2b.h3cw.com
uaiuq.b2b.h3cw.com
vklxn.b2b.h3cw.com
tysuh.b2b.h3cw.com
oedze.b2b.h3cw.com
unnjb.b2b.h3cw.com
rlscz.b2b.h3cw.com
yncaf.b2b.h3cw.com
nrmji.b2b.h3cw.com
rkgmb.b2b.h3cw.com
dwrdd.b2b.h3cw.com
wbdsk.b2b.h3cw.com
gfapp.b2b.h3cw.com
jjfnm.b2b.h3cw.com
inirz.b2b.h3cw.com
yjejs.b2b.h3cw.com
vnweq.b2b.h3cw.com
oubwp.b2b.h3cw.com
gvqpy.b2b.h3cw.com
lxmht.b2b.h3cw.com
uqsxg.b2b.h3cw.com
svawb.b2b.h3cw.com
sytxp.b2b.h3cw.com
ircyx.b2b.h3cw.com
gsohp.b2b.h3cw.com
uujlk.b2b.h3cw.com
kcrqm.b2b.h3cw.com
dsngi.b2b.h3cw.com
dskfv.b2b.h3cw.com
uzvqc.b2b.h3cw.com
zijie.b2b.h3cw.com
nsndc.b2b.h3cw.com
siqvx.b2b.h3cw.com
nmzhw.b2b.h3cw.com
xcrwy.b2b.h3cw.com
kgbah.b2b.h3cw.com
rkyfe.b2b.h3cw.com
yozpu.b2b.h3cw.com
yxmea.b2b.h3cw.com
oachq.b2b.h3cw.com
hhcxw.b2b.h3cw.com
yklml.b2b.h3cw.com
zrtif.b2b.h3cw.com
jcenf.b2b.h3cw.com
zfynf.b2b.h3cw.com
mlnjb.b2b.h3cw.com
mitva.b2b.h3cw.com
ikpla.b2b.h3cw.com
uqlrm.b2b.h3cw.com
yaihw.b2b.h3cw.com
gzvuq.b2b.h3cw.com
aacoo.b2b.h3cw.com
rqrey.b2b.h3cw.com
uhhtb.b2b.h3cw.com
lgpaj.b2b.h3cw.com
mlceq.b2b.h3cw.com
xkmeg.b2b.h3cw.com
obkwf.b2b.h3cw.com
stbtv.b2b.h3cw.com
vqfln.b2b.h3cw.com
xdenf.b2b.h3cw.com
vxwrn.b2b.h3cw.com
zibxc.b2b.h3cw.com
qfqtv.b2b.h3cw.com
vewef.b2b.h3cw.com
hfhwi.b2b.h3cw.com
zvtii.b2b.h3cw.com
ubovn.b2b.h3cw.com
qtyug.b2b.h3cw.com
kayuj.b2b.h3cw.com
bklxz.b2b.h3cw.com
nmiqj.b2b.h3cw.com
hchuw.b2b.h3cw.com
kpdlr.b2b.h3cw.com
uaoav.b2b.h3cw.com
tmhhd.b2b.h3cw.com
xtoti.b2b.h3cw.com
xjpxj.b2b.h3cw.com
kmwfn.b2b.h3cw.com
ixgyh.b2b.h3cw.com
mvxiy.b2b.h3cw.com
jzeny.b2b.h3cw.com
kqllg.b2b.h3cw.com
ibdnc.b2b.h3cw.com
xmenv.b2b.h3cw.com
cjyws.b2b.h3cw.com
zvqpc.b2b.h3cw.com
dikty.b2b.h3cw.com
zcujx.b2b.h3cw.com
haiqc.b2b.h3cw.com
suzvx.b2b.h3cw.com
rdlnm.b2b.h3cw.com
miuwc.b2b.h3cw.com
ekzeg.b2b.h3cw.com
ndhwv.b2b.h3cw.com
jplte.b2b.h3cw.com
csvxg.b2b.h3cw.com
rwrgs.b2b.h3cw.com
okfxt.b2b.h3cw.com
ccukd.b2b.h3cw.com
zincc.b2b.h3cw.com
ngbqs.b2b.h3cw.com
mojvk.b2b.h3cw.com
ynvfo.b2b.h3cw.com
biqfx.b2b.h3cw.com
bqprt.b2b.h3cw.com
cemxh.b2b.h3cw.com
xkcyk.b2b.h3cw.com
bbpek.b2b.h3cw.com
uhjvq.b2b.h3cw.com
kpxgf.b2b.h3cw.com
vujbh.b2b.h3cw.com
vuplq.b2b.h3cw.com
zziuw.b2b.h3cw.com
ppdse.b2b.h3cw.com
uacaj.b2b.h3cw.com
hbcrd.b2b.h3cw.com
pcdgf.b2b.h3cw.com
xwvyq.b2b.h3cw.com
qjews.b2b.h3cw.com
lezzb.b2b.h3cw.com
xtgzh.b2b.h3cw.com
dgufh.b2b.h3cw.com
yhjvo.b2b.h3cw.com
dvkzy.b2b.h3cw.com
fvqir.b2b.h3cw.com
cidpr.b2b.h3cw.com
plwfu.b2b.h3cw.com
uwasi.b2b.h3cw.com
dsmln.b2b.h3cw.com
ciwfk.b2b.h3cw.com
zsbnp.b2b.h3cw.com
wcxtl.b2b.h3cw.com
hjxdy.b2b.h3cw.com
mvxcl.b2b.h3cw.com
cyjlk.b2b.h3cw.com
ajpkx.b2b.h3cw.com
gmbag.b2b.h3cw.com
jldgs.b2b.h3cw.com
wmuzb.b2b.h3cw.com
jwrdm.b2b.h3cw.com
gsago.b2b.h3cw.com
qiiuj.b2b.h3cw.com
hkoqw.b2b.h3cw.com
vzhkw.b2b.h3cw.com
svzto.b2b.h3cw.com
wrjle.b2b.h3cw.com
zlgzz.b2b.h3cw.com
hnixp.b2b.h3cw.com
letya.b2b.h3cw.com
eklbg.b2b.h3cw.com
qsbnv.b2b.h3cw.com
vujrk.b2b.h3cw.com
ihdfk.b2b.h3cw.com
rfkdq.b2b.h3cw.com
wajed.b2b.h3cw.com
nwkqy.b2b.h3cw.com
qjegf.b2b.h3cw.com
lbcuq.b2b.h3cw.com
npobx.b2b.h3cw.com
nwvkf.b2b.h3cw.com
lvohw.b2b.h3cw.com
tjbkj.b2b.h3cw.com
fsami.b2b.h3cw.com
zzatp.b2b.h3cw.com
fzhni.b2b.h3cw.com
onogi.b2b.h3cw.com
rcxzr.b2b.h3cw.com
gdikp.b2b.h3cw.com
fluav.b2b.h3cw.com
lxfgf.b2b.h3cw.com
xmozv.b2b.h3cw.com
ioxpy.b2b.h3cw.com
qshmo.b2b.h3cw.com
ushmv.b2b.h3cw.com
kjrms.b2b.h3cw.com
mlfut.b2b.h3cw.com
emubn.b2b.h3cw.com
zrmbc.b2b.h3cw.com
kthnp.b2b.h3cw.com
dpqdy.b2b.h3cw.com
erson.b2b.h3cw.com
cshfb.b2b.h3cw.com
qjxxt.b2b.h3cw.com
btcej.b2b.h3cw.com
nwkwf.b2b.h3cw.com
snphj.b2b.h3cw.com
vhcej.b2b.h3cw.com
dirrg.b2b.h3cw.com
uajbv.b2b.h3cw.com
ytihj.b2b.h3cw.com
csxfr.b2b.h3cw.com
uwedm.b2b.h3cw.com
rdmht.b2b.h3cw.com
cbxmr.b2b.h3cw.com
yetgo.b2b.h3cw.com
ynlno.b2b.h3cw.com
mstsu.b2b.h3cw.com
glnhq.b2b.h3cw.com
qpkmy.b2b.h3cw.com
iegeg.b2b.h3cw.com
agtow.b2b.h3cw.com
kqfix.b2b.h3cw.com
agvut.b2b.h3cw.com
sltvh.b2b.h3cw.com
xpexm.b2b.h3cw.com
wzrtc.b2b.h3cw.com
mcuwb.b2b.h3cw.com
nvfyv.b2b.h3cw.com
mpodj.b2b.h3cw.com
azbjv.b2b.h3cw.com
zzopo.b2b.h3cw.com
piqfr.b2b.h3cw.com
kkyka.b2b.h3cw.com
xtbhj.b2b.h3cw.com
qietd.b2b.h3cw.com
rrzlu.b2b.h3cw.com
ravbd.b2b.h3cw.com
awoxc.b2b.h3cw.com
tfuzo.b2b.h3cw.com
rksrn.b2b.h3cw.com
kmntz.b2b.h3cw.com
rkyaz.b2b.h3cw.com
egbhg.b2b.h3cw.com
exekm.b2b.h3cw.com
oowbq.b2b.h3cw.com
hgoxi.b2b.h3cw.com
atogv.b2b.h3cw.com
gidpr.b2b.h3cw.com
zynmh.b2b.h3cw.com
ekxge.b2b.h3cw.com
hgpaj.b2b.h3cw.com
yrttr.b2b.h3cw.com
sezbw.b2b.h3cw.com
vhjfd.b2b.h3cw.com
hjrrj.b2b.h3cw.com
pucyd.b2b.h3cw.com
oaipr.b2b.h3cw.com
miqjs.b2b.h3cw.com
ydmkt.b2b.h3cw.com
kzfat.b2b.h3cw.com
编辑平滑曲线
上面我们已经完成了平滑曲线的点集模拟,连接起来就是一条曲线。在一些业务场景中,需要以曲线上的点为控制操作点,移动点以达到编辑曲线。
在曲线上设置多个操作点。如何在曲线上设置期望位置的点,可以看 .NET 曲线上的点- 获取距离最近的点 - 唐宋元明清2188 - 博客园。点击时,获取曲线离点击位置最近的点即可
选择点后,操作控制点移动。曲线根据操作点的位置变更,重新生成新的曲线。曲线编辑效果如下图:
重新生成曲线这部分代码没有啥难点,核心代码就是上面的平滑曲线模拟。看上面仓库代码即可
参考文章:
GDI+ 中的贝塞尔自由绘制曲线 - Windows Forms .NET Framework | Microsoft Learn
Bezier曲线反求控制点_如何反求贝塞尔曲线的控制点-CSDN博客