Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Help to analyze how a software/program constructs Bezier curve

Tags:

c#

delphi

bezier

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-captureC# DrawBezier_3 screen-captureC# DrawBezier_4 screen-capture

like image 617
SOUser Avatar asked Aug 21 '11 20:08

SOUser


1 Answers

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

like image 60
Dani Avatar answered Oct 21 '22 02:10

Dani