帮助分析软件/程序如何构建贝塞尔曲线

发布于 2024-11-30 23:10:04 字数 6931 浏览 2 评论 0原文

我试图了解由 cambridgesoft 开发的业界领先的化学工具 ChemDraw 如何构建贝塞尔曲线,以便我可以手动将其他程序/例程(例如自制的 Delphi/C# 实用程序)中的贝塞尔曲线点转换为ChemDraw 可识别的曲线数据。在开始之前,我必须承认我是在问某些黑盒内部是如何工作的,因此对任何麻烦表示歉意并感谢任何帮助!

我在ChemDraw中制作了四种最简单的贝塞尔曲线,并将它们保存为.CDXML文件,其曲线部分已粘贴在最后。 .CDXML 文件和相应的图片都可以从 fileserve 下载。 在此处下载 Bezier_curve_ChemDraw_sampleChemDraw 试用版可在此处下载

问题

问题1.以“Line”类型的曲线点为例,我在制作这条曲线时总共使用了两个点。为什么 ChemDraw 为其存储六个点? ChemDraw 是如何准确解释这些点的呢?其他三种类型也存在同样的问题,最多使用四个显式点。

问题2. 如果ChemDraw 中显示“使用三点的贝塞尔曲线-第一种类型”的内容,可以看到第一个点不可能是(12.22, 104.25)。我的意思是,它的X坐标明显大于12。为什么会发生这种情况呢? ChemDraw 是否对数据进行一些转换? “使用四点的贝塞尔曲线”也存在同样的问题。

问题 3. CurvePoints 属性末尾的额外空白字符似乎有所不同。到底有什么区别呢?

有人可以告诉我这些问题吗?

ChemDraw .CDXML 文件中的曲线部分

============ 线 ==============
Line

 <curve  
 id="2"  
 Z="1"  
 ArrowheadType="Solid"  
 CurvePoints="143.47 116.25 143.47 116.25 143.47 116.25 300.22 117.75 300.22 117.75 300.22 117.75"  
 />  

============ 使用三点的贝塞尔曲线 - 第一种类型 ===== =========
曲线-三点-拖动起点

 <curve  
 id="2"  
 Z="1"  
 ArrowheadType="Solid"  
 CurvePoints="12.22 104.25 121.72 106.5 231.22 108.75 230.47 204 230.47 204 230.47 204"  
 />  

============ 使用三点的贝塞尔曲线- 第二种==============
曲线-三点-拖动停止点

 <curve  
 id="2"  
 Z="1"  
 ArrowheadType="Solid"  
 CurvePoints="134.47 97.5 134.47 97.5 134.47 97.5 231.22 60.75 229.72 109.5 228.22 158.25"  
 />  

============ 使用四点的贝塞尔曲线==============
曲线 - 三点 - 拖动起点和终点

 <curve  
 id="2"  
 Z="1"  
 ArrowheadType="Solid"  
 CurvePoints="5.47 93.75 123.22 93.75 240.97 93.75 351.22 177.75 236.47 177.75 121.72 177.75"   
 />  

生成相同曲线的示例 C# 程序

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WinForms_2_DrawBezier
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Text = "Draw Bezier Curve";
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            // DrawBezier_1(this, e);
            // DrawBezier_2(this, e);
            // DrawBezier_3(this, e);
             DrawBezier_4(this, e);
        }

        private void DrawBezier_1(object sender, PaintEventArgs e)
        {
            Pen l_pen = new Pen(Color.Black);
            PointF l_pt1 = new PointF(143.47f, 116.25f);
            PointF l_pt2 = new PointF(l_pt1.X, l_pt1.Y);
            PointF l_pt3 = new PointF(l_pt1.X, l_pt1.Y);
            PointF l_pt4 = new PointF(300.22f, 117.75f);
            PointF l_pt5 = new PointF(l_pt4.X, l_pt4.Y);
            PointF l_pt6 = new PointF(l_pt4.X, l_pt4.Y);

            PointF[] l_pts = new PointF[6];
            l_pts[0] = l_pt1;
            l_pts[1] = l_pt2;
            l_pts[2] = l_pt3;
            l_pts[3] = l_pt4;
            l_pts[4] = l_pt5;
            l_pts[5] = l_pt6;

            // e.Graphics.DrawBezier(l_pen, l_pt1, l_pt2, l_pt5, l_pt6);

            e.Graphics.DrawBezier(l_pen, l_pt1, l_pt3, l_pt4, l_pt6); // As suggested by Dani

            // e.Graphics.DrawBeziers(l_pen, l_pts);
        }

        private void DrawBezier_2(object sender, PaintEventArgs e)
        {
            Pen l_pen = new Pen(Color.Black);
            PointF l_pt1 = new PointF(12.22f, 104.25f);
            PointF l_pt2 = new PointF(121.72f, 106.5f);
            PointF l_pt3 = new PointF(231.22f, 108.75f);
            PointF l_pt4 = new PointF(230.47f, 204f);
            PointF l_pt5 = new PointF(l_pt4.X, l_pt4.Y);
            PointF l_pt6 = new PointF(l_pt4.X, l_pt4.Y);

            PointF[] l_pts = new PointF[6];
            l_pts[0] = l_pt1;
            l_pts[1] = l_pt2;
            l_pts[2] = l_pt3;
            l_pts[3] = l_pt4;
            l_pts[4] = l_pt5;
            l_pts[5] = l_pt6;

            // e.Graphics.DrawBezier(l_pen, l_pt1, l_pt2, l_pt3, l_pt4);

            e.Graphics.DrawBezier(l_pen, l_pt1, l_pt3, l_pt4, l_pt6); // As suggested by Dani

            // e.Graphics.DrawBeziers(l_pen, l_pts);
        }

        private void DrawBezier_3(object sender, PaintEventArgs e)
        {
            Pen l_pen = new Pen(Color.Black);
            PointF l_pt1 = new PointF(134.47f, 97.5f);
            PointF l_pt2 = new PointF(l_pt1.X, l_pt1.Y);
            PointF l_pt3 = new PointF(l_pt1.X, l_pt1.Y);
            PointF l_pt4 = new PointF(231.22f, 60.75f);
            PointF l_pt5 = new PointF(229.72f, 109.5f);
            PointF l_pt6 = new PointF(228.22f, 158.25f);

            PointF[] l_pts = new PointF[6];
            l_pts[0] = l_pt1;
            l_pts[1] = l_pt2;
            l_pts[2] = l_pt3;
            l_pts[3] = l_pt4;
            l_pts[4] = l_pt5;
            l_pts[5] = l_pt6;

            // e.Graphics.DrawBezier(l_pen, l_pt3, l_pt4, l_pt5, l_pt6);

            e.Graphics.DrawBezier(l_pen, l_pt1, l_pt3, l_pt4, l_pt6); // As suggested by Dani

            // e.Graphics.DrawBeziers(l_pen, l_pts);
        }

        private void DrawBezier_4(object sender, PaintEventArgs e)
        {
            Pen l_pen = new Pen(Color.Black);
            PointF l_pt1 = new PointF(5.47f, 93.75f);
            PointF l_pt2 = new PointF(123.22f, 93.75f);
            PointF l_pt3 = new PointF(240.97f, 93.75f);
            PointF l_pt4 = new PointF(351.22f, 177.75f);
            PointF l_pt5 = new PointF(236.47f, 177.75f);
            PointF l_pt6 = new PointF(121.72f, 177.75f);

            PointF[] l_pts = new PointF[6];
            l_pts[0] = l_pt1;
            l_pts[1] = l_pt2;
            l_pts[2] = l_pt3;
            l_pts[3] = l_pt4;
            l_pts[4] = l_pt5;
            l_pts[5] = l_pt6;

            // e.Graphics.DrawBezier(l_pen, l_pt1, l_pt4, l_pt5, l_pt6);

            e.Graphics.DrawBezier(l_pen, l_pt1, l_pt3, l_pt4, l_pt6); // As suggested by Dani

            // e.Graphics.DrawBeziers(l_pen, l_pts);
        }
    }
}

但是,我无法制作此 C# 实用程序生成的曲线与 ChemDraw 显示的曲线相同,但第一个 Line 类型除外。
C# DrawBezier_1 屏幕捕获
C# DrawBezier_2 屏幕捕获 C# DrawBezier_3 屏幕捕获 C# DrawBezier_4 屏幕捕获

I am trying to understand how ChemDraw, a Industry-Leading Chemistry Tool developed by cambridgesoft, constructs Bezier curves, so that I can manually translate the Bezier curve points from other programs/routines (such as home-made Delphi/C# utilities) into the curve data recognizable for ChemDraw. Before starting, I must admit that I am asking how certain blackbox works internally, and therefore want to appologize for any trouble and appreciate any help!

I have made in ChemDraw four types of simplest Bezier curves and saved them as .CDXML file, the curve section of which have been pasted in the end. Both the .CDXML files and corresponding pictures can be downloaded from fileserve. Download Bezier_curve_ChemDraw_sample here. ChemDraw trial edition can be downloaded here.

Questions

Question 1. Take the curve points of the "Line" type for example, there are overall two points that I used when making this curve. Why does ChemDraw store six points for it? And how does ChemDraw interpret these points exactly? The same question exists for the other three types, which employs four explicit points at most.

Question 2. If the content of "Bezier curve using three points - first type" is displayed in ChemDraw, one can see the first point can not be (12.22, 104.25). I mean, its X coordinate is obviously larger than 12. Why is this happening? Does ChemDraw do some transformation with the data? The same question exists for "Bezier curve using four points".

Question 3. It seems the extra blank character at the end of the CurvePoints attribute makes difference. What is the difference exactly?

Could some one enlighten me about these problems?

Curve sections in the ChemDraw .CDXML files

============ Line ==============
Line

 <curve  
 id="2"  
 Z="1"  
 ArrowheadType="Solid"  
 CurvePoints="143.47 116.25 143.47 116.25 143.47 116.25 300.22 117.75 300.22 117.75 300.22 117.75"  
 />  

============ Bezier curve using three points - first type ==============
Curve - three points - drag the starting point

 <curve  
 id="2"  
 Z="1"  
 ArrowheadType="Solid"  
 CurvePoints="12.22 104.25 121.72 106.5 231.22 108.75 230.47 204 230.47 204 230.47 204"  
 />  

============ Bezier curve using three points - second type ==============
Curve - three points - drag the stopping point

 <curve  
 id="2"  
 Z="1"  
 ArrowheadType="Solid"  
 CurvePoints="134.47 97.5 134.47 97.5 134.47 97.5 231.22 60.75 229.72 109.5 228.22 158.25"  
 />  

============ Bezier curve using four points ==============
Curve - three points - drag both the starting and the stopping points

 <curve  
 id="2"  
 Z="1"  
 ArrowheadType="Solid"  
 CurvePoints="5.47 93.75 123.22 93.75 240.97 93.75 351.22 177.75 236.47 177.75 121.72 177.75"   
 />  

Example C# program to generate the same curves

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WinForms_2_DrawBezier
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Text = "Draw Bezier Curve";
        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            // DrawBezier_1(this, e);
            // DrawBezier_2(this, e);
            // DrawBezier_3(this, e);
             DrawBezier_4(this, e);
        }

        private void DrawBezier_1(object sender, PaintEventArgs e)
        {
            Pen l_pen = new Pen(Color.Black);
            PointF l_pt1 = new PointF(143.47f, 116.25f);
            PointF l_pt2 = new PointF(l_pt1.X, l_pt1.Y);
            PointF l_pt3 = new PointF(l_pt1.X, l_pt1.Y);
            PointF l_pt4 = new PointF(300.22f, 117.75f);
            PointF l_pt5 = new PointF(l_pt4.X, l_pt4.Y);
            PointF l_pt6 = new PointF(l_pt4.X, l_pt4.Y);

            PointF[] l_pts = new PointF[6];
            l_pts[0] = l_pt1;
            l_pts[1] = l_pt2;
            l_pts[2] = l_pt3;
            l_pts[3] = l_pt4;
            l_pts[4] = l_pt5;
            l_pts[5] = l_pt6;

            // e.Graphics.DrawBezier(l_pen, l_pt1, l_pt2, l_pt5, l_pt6);

            e.Graphics.DrawBezier(l_pen, l_pt1, l_pt3, l_pt4, l_pt6); // As suggested by Dani

            // e.Graphics.DrawBeziers(l_pen, l_pts);
        }

        private void DrawBezier_2(object sender, PaintEventArgs e)
        {
            Pen l_pen = new Pen(Color.Black);
            PointF l_pt1 = new PointF(12.22f, 104.25f);
            PointF l_pt2 = new PointF(121.72f, 106.5f);
            PointF l_pt3 = new PointF(231.22f, 108.75f);
            PointF l_pt4 = new PointF(230.47f, 204f);
            PointF l_pt5 = new PointF(l_pt4.X, l_pt4.Y);
            PointF l_pt6 = new PointF(l_pt4.X, l_pt4.Y);

            PointF[] l_pts = new PointF[6];
            l_pts[0] = l_pt1;
            l_pts[1] = l_pt2;
            l_pts[2] = l_pt3;
            l_pts[3] = l_pt4;
            l_pts[4] = l_pt5;
            l_pts[5] = l_pt6;

            // e.Graphics.DrawBezier(l_pen, l_pt1, l_pt2, l_pt3, l_pt4);

            e.Graphics.DrawBezier(l_pen, l_pt1, l_pt3, l_pt4, l_pt6); // As suggested by Dani

            // e.Graphics.DrawBeziers(l_pen, l_pts);
        }

        private void DrawBezier_3(object sender, PaintEventArgs e)
        {
            Pen l_pen = new Pen(Color.Black);
            PointF l_pt1 = new PointF(134.47f, 97.5f);
            PointF l_pt2 = new PointF(l_pt1.X, l_pt1.Y);
            PointF l_pt3 = new PointF(l_pt1.X, l_pt1.Y);
            PointF l_pt4 = new PointF(231.22f, 60.75f);
            PointF l_pt5 = new PointF(229.72f, 109.5f);
            PointF l_pt6 = new PointF(228.22f, 158.25f);

            PointF[] l_pts = new PointF[6];
            l_pts[0] = l_pt1;
            l_pts[1] = l_pt2;
            l_pts[2] = l_pt3;
            l_pts[3] = l_pt4;
            l_pts[4] = l_pt5;
            l_pts[5] = l_pt6;

            // e.Graphics.DrawBezier(l_pen, l_pt3, l_pt4, l_pt5, l_pt6);

            e.Graphics.DrawBezier(l_pen, l_pt1, l_pt3, l_pt4, l_pt6); // As suggested by Dani

            // e.Graphics.DrawBeziers(l_pen, l_pts);
        }

        private void DrawBezier_4(object sender, PaintEventArgs e)
        {
            Pen l_pen = new Pen(Color.Black);
            PointF l_pt1 = new PointF(5.47f, 93.75f);
            PointF l_pt2 = new PointF(123.22f, 93.75f);
            PointF l_pt3 = new PointF(240.97f, 93.75f);
            PointF l_pt4 = new PointF(351.22f, 177.75f);
            PointF l_pt5 = new PointF(236.47f, 177.75f);
            PointF l_pt6 = new PointF(121.72f, 177.75f);

            PointF[] l_pts = new PointF[6];
            l_pts[0] = l_pt1;
            l_pts[1] = l_pt2;
            l_pts[2] = l_pt3;
            l_pts[3] = l_pt4;
            l_pts[4] = l_pt5;
            l_pts[5] = l_pt6;

            // e.Graphics.DrawBezier(l_pen, l_pt1, l_pt4, l_pt5, l_pt6);

            e.Graphics.DrawBezier(l_pen, l_pt1, l_pt3, l_pt4, l_pt6); // As suggested by Dani

            // e.Graphics.DrawBeziers(l_pen, l_pts);
        }
    }
}

However, I cannot make this C# utility generate the same curves as ChemDraw displays except for the first Line type.
C# DrawBezier_1 screen-capture
C# DrawBezier_2 screen-capture
C# DrawBezier_3 screen-capture
C# DrawBezier_4 screen-capture

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

╭ゆ眷念 2024-12-07 23:10:04

它使用标准贝塞尔曲线算法,可以采用任意数量的点来构建曲线。看看贝塞尔曲线

编辑:
C# DrawBezier 仅支持二次(四点)贝塞尔曲线。您的四点贝塞尔曲线将使用以下方法转换为 C#:

float[] CurvePoints = GetCurvePoints();

DrawBezier(Pen, CurvePoints[0], CurvePoints[1], CurvePoints[4], CurvePoints[5],
                CurvePoints[6], CurvePoints[7], CurvePoints[10], CurvePoints[11]);

其中 GetCurvePoints() 只为您提供来自 CurvePoints xml 属性的点列表。

编辑2:
重现最后一条曲线 所有曲线的代码:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Linq;
using System.Collections.Generic;

public class Bezier : Form
{
    static public void Main ()
    {
        Application.Run (new Bezier ());
    }

    protected override void OnPaint (PaintEventArgs e)
    {
        // The input with all points
        string CurveDataString = "5.47 93.75 123.22 93.75 240.97 93.75 351.22 177.75 236.47 177.75 121.72 177.75";

        string[] CurveDataStringParts = CurveDataString.Split(' ');

        int[] Keep = {2, 3, 4, 5, 6, 7, 8, 9};
        float[] CurveData = (from i in Keep select float.Parse(CurveDataStringParts[i])).ToArray();

        e.Graphics.DrawBezier(Pens.Black, CurveData[0], CurveData[1], CurveData[2], CurveData[3],
                                          CurveData[4], CurveData[5], CurveData[6], CurveData[7]);

        for(int i = 0; i < CurveData.Length; i += 2)
        {
            e.Graphics.FillEllipse(Brushes.Black, new RectangleF(CurveData[i] - 2, CurveData[i + 1] - 2, 4, 4));
        }

        base.OnPaint (e);
    }
}

结果:
贝塞尔曲线示例

Its using the standard bezier curve algorithm which can take any amount of points to construct a curve. look at Bézier curve

Edit:
C# DrawBezier only supports quadratic (four points) bezier. You four point bezier will be translated to C# using:

float[] CurvePoints = GetCurvePoints();

DrawBezier(Pen, CurvePoints[0], CurvePoints[1], CurvePoints[4], CurvePoints[5],
                CurvePoints[6], CurvePoints[7], CurvePoints[10], CurvePoints[11]);

Where GetCurvePoints() just gives you the list of points from CurvePoints xml attribute.

Edit 2:
Code to reproduce the last curve all curves:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Linq;
using System.Collections.Generic;

public class Bezier : Form
{
    static public void Main ()
    {
        Application.Run (new Bezier ());
    }

    protected override void OnPaint (PaintEventArgs e)
    {
        // The input with all points
        string CurveDataString = "5.47 93.75 123.22 93.75 240.97 93.75 351.22 177.75 236.47 177.75 121.72 177.75";

        string[] CurveDataStringParts = CurveDataString.Split(' ');

        int[] Keep = {2, 3, 4, 5, 6, 7, 8, 9};
        float[] CurveData = (from i in Keep select float.Parse(CurveDataStringParts[i])).ToArray();

        e.Graphics.DrawBezier(Pens.Black, CurveData[0], CurveData[1], CurveData[2], CurveData[3],
                                          CurveData[4], CurveData[5], CurveData[6], CurveData[7]);

        for(int i = 0; i < CurveData.Length; i += 2)
        {
            e.Graphics.FillEllipse(Brushes.Black, new RectangleF(CurveData[i] - 2, CurveData[i + 1] - 2, 4, 4));
        }

        base.OnPaint (e);
    }
}

Result:
Bezier example

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文