2011-08-21 6 views
7

Próbuję zrozumieć, jak ChemDraw, wiodące w branży narzędzie chemiczne opracowane przez cambridgesoft, konstruuje krzywe Beziera, dzięki czemu mogę ręcznie tłumaczyć punkty krzywej Beziera z innych programów/rutyny (takie jak domowe narzędzia Delphi/C#) na dane krzywej rozpoznawalne dla ChemDraw. Zanim zacznę, muszę przyznać, że pytam, w jaki sposób pewna czarna skrzynka działa wewnętrznie, i dlatego chce przeprosić za wszelkie kłopoty i docenić każdą pomoc!Pomoc w analizie, jak program/program konstruuje krzywą Beziera

Wykonałem w ChemDraw cztery rodzaje najprostszych krzywych Beziera i zapisałem je jako plik .CDXML, którego część krzywej została wklejona na końcu. Pliki .CDXML i odpowiadające im zdjęcia można pobrać z fileserve. Download Bezier_curve_ChemDraw_sample here. ChemDraw trial edition can be downloaded here.

Pytania

Pytanie 1. Weź punktów krzywej typu „Line” na przykład, istnieją dwa punkty ogólne, że stosowane przy dokonywaniu tej krzywej. Dlaczego ChemDraw przechowuje dla niego sześć punktów? A jak ChemDraw dokładnie interpretuje te punkty? To samo pytanie dotyczy pozostałych trzech typów, które zatrudniają co najwyżej cztery wyraźne punkty.

Pytanie 2. Jeśli w "ChemDraw" wyświetlana jest treść "Krzywa Beziera za pomocą trzech punktów - pierwszy typ", widać, że pierwszy punkt nie może być (12.22, 104,25). Mam na myśli, że jego współrzędna X jest oczywiście większa niż 12. Dlaczego tak się dzieje? Czy ChemDraw dokonuje pewnych transformacji danymi? To samo pytanie dotyczy "krzywej Beziera za pomocą czterech punktów".

Pytanie 3. Wygląda na to, że dodatkowa pusta postać na końcu atrybutu CurvePoints robi różnicę. Czym dokładnie jest różnica?

Czy ktoś może mnie oświecić w związku z tymi problemami?

odcinki krzywej w plikach ChemDraw .CDXML

============ Linia ==============
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" 
/> 

============ krzywa Beziera za pomocą trzech punktów - pierwszy typ ==============
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" 
/> 

============ Beziera krzywej za pomocą trzech punktów - drugi typ ==============
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" 
/> 

===== ======= krzywa Beziera pomocą czterech punktach ==============
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" 
/> 

Przykład C Program # do wytworzenia tej samej krzywych

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); 
     } 
    } 
} 

Jednak nie mogę sprawić, aby to narzędzie C# generowało takie same krzywe, co wyświetlanie ChemDraw z wyjątkiem pierwszego typu Line.
C# DrawBezier_1 screen-capture
C# DrawBezier_2 screen-capture C# DrawBezier_3 screen-capture C# DrawBezier_4 screen-capture

+2

Upvoted ze względu na szczegółowe pytania. –

Odpowiedz

7

Jest za pomocą standardowego algorytmu krzywej beziera który może przyjmować dowolną ilość punktów skonstruować krzywą. spójrz na Bézier curve

Edytuj:
C# DrawBezier obsługuje tylko kwadratowe (cztery punkty) beziera. Państwo cztery punkt bezier będą tłumaczone na C# przy użyciu:

float[] CurvePoints = GetCurvePoints(); 

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

Gdzie GetCurvePoints() tylko daje listę punktów z CurvePoints atrybutu xml.

Edit 2:
Kod odtworzyć ostatnią krzywą wszystkie krzywe:

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); 
    } 
} 

Wynik:
Bezier example

+0

Bardzo dziękuję za komentarze! Czy odnosisz się do "Wyniesienia stopni"? Czy mogłabyś pomóc ci jeszcze bardziej skomentować sposób przekształcania w praktyce punktów używanych, na przykład, w funkcji C# Graphics.DrawBezier/DrawBeziers do/z punktów używanych w wyżej wymienionym ChemDraw? – SOUser

+0

@ Xhenhen Li: dodano w edycji. – Dani

+0

Dziękuję za opracowanie! Czy masz na myśli, jeśli pozwolę, aby 'GetCurvePoints' zwróciło dane zawarte w atrybucie xml ChemDraw CurvePoints, C# Graphics.DrawBezier wygeneruje taką samą krzywą, jak wyświetlana przez ChemDraw? Przykro mi, ale (1) atrybut ChemDraw CurvePoints xml zawiera sześć punktów dla czterech przykładów zamieszczonych powyżej, (2) 'DrawBezier' akceptuje cztery punkty i (3)' DrawBeziers' wydaje się akceptować 4 + 3n punktów. PS: Próbowałem dostarczyć 'DrawBeziers' z tymi sześcioma punktami i nie mogłem uzyskać tego samego lub nawet poprawnego obrazu. Czy możesz pomóc w komentarzach? – SOUser