Help to analyze how a software/program constructs Bezier curve
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 ==============
<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
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
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
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 开发者_如何学GoPointF(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.
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:
精彩评论