Mathematik online lernen im Mathe-Forum. Nachhilfe online
Startseite » Forum » Bezierkurve-Stützpunkt eines Teilkreises berechnen

Bezierkurve-Stützpunkt eines Teilkreises berechnen

Universität / Fachhochschule

Tags: Bezierkurve, Excel, Gerade, Schnittpunkt, Stützpunkt, Teilkreis

 
Antworten Neue Frage stellen Im Forum suchen
Neue Frage
VerFlixt

VerFlixt aktiv_icon

20:26 Uhr, 07.04.2025

Antworten
Hallo zusammen,
ich bin neu hier, kein Mathematiker, hoffe aber mit meiner Frage hier richtig zu sein.

Es geht um eine Bezierkurve 3. Grades, die einen Viertelkreis von 270 Grad bis 360 Grad darstellt, an den eine horizontale Gerade anschließt.
-> Ein Beispiel ist als Excel_1.png angefügt.

In mehreren Schritten soll die Gerade nun bis zum Kreismittelpunkt abgesenkt, und der Viertelkreis jeweils am Schnittpunkt der Geraden abgeschnitten werden.

Die Stützpunkte X2 und Y3 der Ausgangsform berechne ich mit

Radius r * (4/3) * Tan(45 Grad/2)

Zum Absenken muss ich scheinbar X4,Y4 mit den Schnittpunkt-Koordinaten der Geraden auf dem Kreisbogen belegen, und X3,Y3 dann mit diesem Punkt gleichsetzen, damit ein eckiger Übergang entsteht.
Alle anderen Punkte können unverändert bleiben, bis auf X2, der die Krümmung des restlichen Kreisbogens steuert. Aber hier liege ich leicht daneben, wie in Abbildung
-> Excel_2.png
ersichtlich, denn die Krümmung deckt sich nicht mit dem Viertelkreis.

Die Frage ist im Zusammenhang mit einer Excel-Grafik entstanden, weshalb ich versuche, das Problem ebenfalls über Excel zu lösen, auch wenn es bestimmt geeignetere Software dafür gäbe. Zur Vereinfachung lasse ich die Grafik über ein VBA-Makro zeichnen. Den entsprechenden Modul-Code habe ich nachfolgend angefügt. Die Sub CreateScene zeichnet dann alles. (VBA nutzt X1,Y1 als separaten Startpunkt, und bezeichnet die restlichen Punkte der Kurve mit X1,Y1,X2,Y2,X3,Y3 wodurch sich die Bezeichner gegenüber Standard-Bezier-Bezeichnern verschieben)

Falls ich wichtige Angaben nicht genannt haben sollte, bitte ich um Nachsicht. Dann bitte mitteilen, was fehlt...

Vielen Dank schon mal!

- - - - - - - - - - - - -
'Hier der Code:

Option Explicit

' Gegebene Werte für den Viertelkreis
Private Const radius As Single = 200 ' Radius des Kreises
Private Const centerX As Single = 300 ' X-Koordinate des Kreismittelpunkts
Private Const centerY As Single = 300 ' Y-Koordinate des Kreismittelpunkts

Private ws As Worksheet
Private LineY As Single ' Vertikale Position der Geraden

Public Sub CreateScene()
Dim shp As Shape
Set ws = ThisWorkbook.Sheets(1) ' 1. Arbeitsblatt zum Zeichnen auswählen
' Alte Shapes mit Bezierkurven entfernen
For Each shp In ThisWorkbook.Sheets(1).Shapes
shp.Delete
Next
' Neue Shapes mit Bezierkurven zeichnen
For LineY = centerY - radius To centerY Step (centerY - radius) / 4
CreateQuarterCircleAndLine
If LineY = centerY - radius Then
' Erstes Shape in Orange
ws.Shapes(ws.Shapes.Count).Line.ForeColor.RGB = RGB(255, 60, 0)
End If
Next
End Sub

Private Function BerechneKontrollpunktVonLinie(ByVal radius As Double, ByVal yLinie As Double, ByVal yMitte As Double) As Double
Dim winkelRad As Double
Dim abstand As Double
Dim diffY As Double
Dim unterWurzel As Double
' Differenz zwischen yLinie und Kreismitte
diffY = yLinie - yMitte
unterWurzel = radius * radius - diffY * diffY
' Sonderfall: Falls die Linie genau am oberen oder unteren Rand des Kreises verläuft
If unterWurzel <= 0 Then
winkelRad = 1.57079632679 ' Pi/2 Radianten für 90 Grad (Grenzfall)
Else
' Winkel des Schnittpunkts berechnen
winkelRad = Atn(diffY / Sqr(unterWurzel))
End If
' Kontrollpunkt-Abstand berechnen
abstand = radius * (4 / 3) * Tan(winkelRad / 4)
' Rückgabe des berechneten Abstands
BerechneKontrollpunktVonLinie = abstand
End Function

Private Function HorizontalArcIntersectionPoints( _
ByVal circleCenterX As Double, _
ByVal circleCenterY As Double, _
ByVal circleRadius As Double, _
ByVal LineY As Double) As Variant
' Die Schnittpunkte einer horizontalen Geraden mit einem Kreis berechnen
Dim DeltaY As Double
Dim DeltaX As Double
Dim point1 As Variant
Dim point2 As Variant
' Überprüfen, ob die Linie überhaupt den Kreisbogen berührt
DeltaY = Abs(circleCenterY - LineY)
If DeltaY > circleRadius Then
' Kein Schnittpunkt, da die Linie außerhalb des Kreisbogens liegt
HorizontalArcIntersectionPoints = Null
Exit Function
End If
' Sonst X-Koordinaten der Schnittpunkte berechnen
DeltaX = Sqr(circleRadius ^ 2 - DeltaY ^ 2)
point1 = Array(circleCenterX - DeltaX, LineY)
point2 = Array(circleCenterX + DeltaX, LineY)
' Rückgabe beider Schnittpunkte
HorizontalArcIntersectionPoints = Array(point1, point2)
End Function

Private Sub CreateQuarterCircleAndLine()
Const PI = 3.14159265358979

Dim intersectionPointsLeft As Variant ' Punkt auf einem Kreisbogen
Dim intersectionPointsRight As Variant ' Punkt auf einem Kreisbogen
Dim X1 As Single, Y1 As Single
Dim X2 As Single, Y2 As Single
Dim X3 As Single, Y3 As Single
Dim bezierPoint As Single
Dim bezierOffset As Double
Dim abstand As Double

' Ein echter Kreis kann durch Bézier-Kurven nicht exakt dargestellt, aber gut angenähert werden.
' Für einen mathematisch perfekten Viertelkreis beträgt der ideale Abstand des Kontrollpunkts
' etwa das 0.55228-fache des Radius ((4/3) * Tan(45 Grad/2) = 0.5522847498 gerundet).
' Bei einem Radius von 18.20544 kann die Distanz d wie folgt berechnet werden:
' d = r * (4/3) * Tan(45 Grad/2)
' d = 18.20544 * 1.33333 * 0.41421
' d = 18.20544 * 0.55228
' d = 10.054
bezierPoint = radius * 0.5522847498 ' Bézier-Kontrollpunkt

abstand = BerechneKontrollpunktVonLinie(radius, LineY, centerY)

' Schnittpunkt auf Kreisbogen berechnen
intersectionPointsLeft = HorizontalArcIntersectionPoints( _
centerX, _
centerY, _
radius, _
LineY)
intersectionPointsRight = HorizontalArcIntersectionPoints( _
centerX, _
centerY, _
radius, _
LineY)
If IsNull(intersectionPointsLeft) Then Exit Sub
If IsNull(intersectionPointsRight) Then Exit Sub

' Berechnung der Kontrollpunkte und Endpunkte
X1 = centerX - radius

If LineY = centerY - radius Then
Y1 = centerY - bezierPoint
X2 = centerX - abstand
Else
bezierOffset = radius * (4 / 3) * Tan(PI / 8)
Y1 = centerY - radius + bezierOffset
X2 = intersectionPointsLeft(0)(0)
End If

Y2 = LineY
X3 = intersectionPointsLeft(0)(0)
Y3 = LineY ' Kurve endet auf Höhe von LineY

' Erstellung der FreeForm mit dem msoSegmentCurve-Segment
Dim freeForm As FreeFormBuilder
Set freeForm = ws.Shapes.BuildFreeform(msoEditingCorner, centerX - radius, centerY)
' Hinzufügen der Nodes für die Kurve
freeForm.AddNodes msoSegmentCurve, msoEditingCorner, _
X1, Y1, _
X2, Y2, _
X3, Y3

' Hinzufügen des horizontalen Liniensegments mit eckigem Übergang
freeForm.AddNodes msoSegmentLine, msoEditingCorner, centerX + radius * 2, LineY

' Konvertieren in eine Shape
Dim Shape As Shape
Set Shape = freeForm.ConvertToShape
End Sub


Excel_1
Excel_2

Für alle, die mir helfen möchten (automatisch von OnlineMathe generiert):
"Ich möchte die Lösung in Zusammenarbeit mit anderen erstellen."
Hierzu passend bei OnlineMathe:
Schnittpunkte bestimmen

Online-Übungen (Übungsaufgaben) bei unterricht.de:
 
Online-Nachhilfe in Mathematik
Antwort
Roman-22

Roman-22

21:50 Uhr, 08.04.2025

Antworten
Also wenn r den Kreisradius bezeichnet und h die Höhe des Schnittpunkts der waagerechte Linie mit dem Kreis, dann erhalte ich für den gesuchten Abstand d den hübschen Ausdruck

d=43hrr-r2-h2(2rr+r2-h2-1)

Für h=r, als den kompletten Viertelkreis, stellt sich dann damit auch in der Tat der von dir angegebene Wert 43r(2-1)0.5522847498307933984r ein.

Auf die Formel kommt man mit der Forderung, dass sich in der Darstellung der Bezierkurve
X(t):=(1-t3)X1+3(1-t)2tX2+3(1-t)t2X3+t3X4
mit t=0.5 der Kreispunkt in der Mitte des Kreisbogens einstellen soll, X1 und X4 die Endpunkte des Kreisbogens sind und X2 und X3 Punkte auf den Kreistangenten in X1 und X4 aus Symmetriegründen im gleichen Abstand d von den jeweiligen Endpunkten.

Dieser Abstand ist also natürlich auf den Kreistangenten in den Endpunkten des zu nähernden Kreisbogens abzutragen (siehe Skizze).

Die Endpunkte haben im gezeigten KS die Koordinaten (r2-h2|h) und (r|0), die beiden Führungspunkte (r2-h2+dhr|h-dr2-h2r) und (r|d).

B


Frage beantwortet
VerFlixt

VerFlixt aktiv_icon

23:44 Uhr, 09.04.2025

Antworten
@Roman-22
Vielen Dank für die gute Erklärung! Es müssen also auch für Kreissegmente, die kleiner als 90 Grad sind, immer alle 8 Werte der Bezierkurve ermittelt werden. Da lag ich komplett falsch.
Hilfreich ist auch der Hinweis, dass wenn h = r, ein kompletter Viertelkreis vorliegt, da meine Prüfung dafür viel zu umständlich war. In der beschriebenen Problemstellung gilt sogar, wenn h >= r, liegt ein kompletter Viertelkreis vor.
Das hilft mir weiter!

VerFlixt

VerFlixt aktiv_icon

11:15 Uhr, 10.04.2025

Antworten
Ich habe die Formel jetzt als VBA-Code umgesetzt, und erhalte für eine Stichprobe im Überwachungsfenster jetzt die richtigen gerundeten Ergebnisse:

Kontrollpunkt-Distanz wenn Radius r=100 und Höhe h=0:=0
Kontrollpunkt-Distanz wenn Radius r=100 und Höhe h=1:0,33333958
Kontrollpunkt-Distanz wenn Radius r=100 und Höhe h=2:0,66671668
Kontrollpunkt-Distanz wenn Radius r=100 und Höhe h=3:1,00016882
Kontrollpunkt-Distanz wenn Radius r=100 und Höhe h=4:1,33373363
Kontrollpunkt-Distanz wenn Radius r=100 und Höhe h=5:1,66744881
Kontrollpunkt-Distanz wenn Radius r=100 und Höhe h=6:2,00135223
Kontrollpunkt-Distanz wenn Radius r=100 und Höhe h=7:2,33548191
Kontrollpunkt-Distanz wenn Radius r=100 und Höhe h=8:2,66987609
Kontrollpunkt-Distanz wenn Radius r=100 und Höhe h=9:3,00457325
Kontrollpunkt-Distanz wenn Radius r=100 und Höhe h=10:3,33961215

Als nächstes passe ich den urspünglichen Code an.

Hier schon mal der Excel-VBA-Code mit der Formelumsetzung:

'--------------------------------
Option Explicit

Public Sub TestBezierPointDistance()

Dim r As Double
Dim h As Double
Dim d As Double

r=100' Radius =100

For h=0 To 10 Step 1' Höhe stufenweise um 1 erhöhen, und gerundeten Kontrollpunkt-Distanz ausgeben.
d= GetBezierPointDistance(r, h)
Debug.Print "Kontrollpunkt-Distanz wenn Radius r= " &r& " und Höhe h= " &h& ": d= " & Round(d, 8)
Next

End Sub

Private Function GetBezierPointDistance(ByVal radius As Double, ByVal height As Double) As Double
' Berechnet die Distanz eines Bezier-Kontrollpunkts

' Variablen:
'd= distanz
'r= radius
'h= height

If radius =0 Or height =0 Then
'1. Sonderfall: Der Radius ist 0, oder die Höhe der Linie entspricht dem Kreis-Mittelpunkt
GetBezierPointDistance =0
Else
If height radius Then
'2. Sonderfall: Die Linie verläuft genau am oberen Rand des Kreises
GetBezierPointDistance =(43) radius (Sqr(2) -1)' Abstand für einen kompletten Viertelkreis berechnen
Else
' Abstand für einen Teilkreis kleiner 90 Grad berechnen
GetBezierPointDistance =(43) (height radius / (radius - Sqr(radius radius - height height)) ((Sqr(2 radius) / Sqr(radius + Sqr(radius radius - height height)) -1)))
End If
End If
End Function

Frage beantwortet
VerFlixt

VerFlixt aktiv_icon

22:37 Uhr, 10.04.2025

Antworten
So, hier noch der überarbeitete VBA-Code, der in Excel jetzt alles wie gewünscht zeichnet.
Es ist wie immer, kaum macht man es richtig, schon geht es.
Vielen Dank nochmal an Roman-22!
Das Ergebnis wieder als Bild-Anhang:

'----------------------------
Option Explicit

' Werte für den Viertelkreis
Private Const radius As Single =200' Radius des Kreises
Private Const centerX As Single =300' X-Koordinate des Kreismittelpunkts
Private Const centerY As Single =300' Y-Koordinate des Kreismittelpunkts

Private ws As Worksheet
Private left As Double
Private top As Double

Public Sub Test()
Dim height As Double

Set ws = ThisWorkbook.Sheets(1) ' Zeichne im ersten Tabellenblatt der Arbeitsmappe
Dim shp As Shape
For Each shp In ws.Shapes
shp.Delete
Next

left = centerX - radius ' Berechnung für den linken Kreisrand
top = centerY - radius ' Berechnung für den oberen Kreisrand

For height =0 To radius Step 25
DrawPartialCircleAndLine radius, height, left, top
If height = radius Then
' Basis Shape in Farbe Orange und in den Hintergrund stellen
ws.Shapes(ws.Shapes.Count).Line.ForeColor.RGB = RGB(255, 60,0)
ws.Shapes(ws.Shapes.Count).ZOrder msoSendToBack
End If
Next

Set ws = Nothing ' Arbeitsspeicher freigeben
End Sub

Private Function GetBezierCurveSegmentPoints(ByVal radius As Double, ByVal height As Double) As Variant
' Berechne die Endpunkte und Führungspunkte einer Bezierkurve 3. Grades
Dim point1 As Variant
Dim point2 As Variant
Dim point3 As Variant
Dim point4 As Variant
Dim distance As Double

' Berechne die Distanz der Bezier-Kontrollpunkte im Teilkreis
If radius =0 Or height =0 Then
'1. Sonderfall: Der Radius ist 0, oder die Gerade liegt auf dem Kreis-Mittelpunkt
distance =0
' Koordinaten von Startpunkt und Endpunkt berechnen
point1 = Array(radius, -height)
point4 = Array(-Sqr(radius radius - height height), -height)
' Koordinaten der Führungspunkte berechnen
point2 = Array(radius, -height)
point3 = Array(radius, -height)
Else
If height radius Then
'2. Sonderfall: Die Linie verläuft genau am oberen Rand des Kreises
distance =(43) radius (Sqr(2) -1)' Abstand für einen kompletten Viertelkreis
If height > radius Then
' Wenn die Gerade den Teilkreis nicht schneidet, von einem Viertelkreis ausgehen
height = radius
End If
Else
' Abstand für einen Teilkreis kleiner 90 Grad berechnen
distance =(43) (height radius / (radius - Sqr(radius radius - height height)) ((Sqr(2 radius) / Sqr(radius + Sqr(radius radius - height height)) -1)))
End If

' Kreissegment links oben
' Koordinaten von Startpunkt und Endpunkt berechnen
point1 = Array(-radius, 0)
point4 = Array(-Sqr(radius radius - height height), -height)
' Koordinaten der Führungspunkte berechnen
point2 = Array(-radius, -distance)
point3 = Array(-Sqr(radius radius - height height) - distance (height / radius), -height + distance Sqr(radius radius - height height) / radius)
End If

GetBezierCurveSegmentPoints = Array(point1, point2, point3, point4)
End Function

Private Sub DrawPartialCircleAndLine(ByVal radius As Double, ByVal height As Double, ByVal left As Double, ByVal top As Double)
' Enpunkte und Kontrollpunkte des Bézier-Segments
Dim X0 As Single, Y0 As Single
Dim X1 As Single, Y1 As Single
Dim X2 As Single, Y2 As Single
Dim X3 As Single, Y3 As Single
Dim Bezierpoints As Variant
Bezierpoints = GetBezierCurveSegmentPoints(radius, height)
If IsEmpty(Bezierpoints) Then Eξt Sub

' Berechnung des Startpunkts
X0= left + Bezierpoints(0)(0) + radius
Y0= top + Bezierpoints(0)(1) + radius

' Berechnung der Kontrollpunkte für das Bézier-Segment
X1= left + Bezierpoints(1)(0) + radius
Y1= top + Bezierpoints(1)(1) + radius

X2= left + Bezierpoints(2)(0) + radius
Y2= top + Bezierpoints(2)(1) + radius

' Berechnung des Endpunkts für das Bézier-Segment
X3= left + Bezierpoints(3)(0) + radius
Y3= top + Bezierpoints(3)(1) + radius

' Erstellung der FreeForm mit dem msoSegmentCurve-Segment
Dim freeForm As FreeFormBuilder
Set freeForm = ws.Shapes.BuildFreeform(msoEditingCorner, X0,Y0)

' Hinzufügen der Nodes für die Kurve
freeForm.AddNodes MsoSegmentType.msoSegmentCurve, MsoEditingType.msoEditingCorner, _
X1,Y1,
X2,Y2,
X3,Y3

' Hinzufügen des horizontalen Liniensegments mit eckigem Übergang oben
freeForm.AddNodes MsoSegmentType.msoSegmentLine, MsoEditingType.msoEditingCorner, left + radius 3,Y3

' Konvertieren in eine Shape
Dim Shape As Shape
Set Shape = freeForm.ConvertToShape

End Sub


Excel_3
Antwort
Roman-22

Roman-22

02:29 Uhr, 11.04.2025

Antworten
Worum geht es dir eigentlich im Endeffekt?
Doch nicht nur um die Erstellung dieser Zeichnung, oder?
Ich hab nicht viel Erfahrung mit VBA in Excel, aber Kreisbögen sollten sich doch einfach mit "<Shape-Objekt>.DrawCircularArc(...)" erstellen lassen und der dafür nötige Zentriwinkel errechnet sich mit arcsin(hr).
Warum also die Bezierkurven?
VerFlixt

VerFlixt aktiv_icon

21:41 Uhr, 11.04.2025

Antworten
Ja, es geht tatsächlich um ein komplexeres Thema.
Durch mein Hobby habe ich umgekehrt zwar Kenntnisse im Zusammenhang mit Excel und VBA, aber eher nicht in mathematischem Formelwerk.

Vorhaben:
Ich möchte ein Steuerelement zur Anzeige von Kraftstofftank-Füllständen in Excel-VBA erstellen. Wenn also beispielsweise eine Reihe von Kraftstoff-Tanks überwacht werden soll, um zu erkennen, das einer der 70.000 Liter Tanks davon nur noch zu 13 Prozent mit Diesel befüllt ist.
Am einfachsten wäre, dies über die textuelle Ausgabe des Prozentwertes zu lösen, oder wenn es grafisch sein soll, über zwei übereinanderliegende, unterschiedlich gefärbte Rechtecke. Aber einigermaßen professionell wird es eben erst mit etwas Aufwand, durch den auch noch was dazugelernt wird.
Ziel ist daher die Erstellung einer grafischen Tankfüllstands-Anzeige mit abgerundeten Ecken ungefähr so, wie im angehängten ersten Beispiel-Bild "Füllstand_1.png".
Ausgehend von zwei übereinanderliegenden gerundeten Rechtecken wie im zweiten Beispiel-Bild "Füllstand_2.png".

Es gibt in Excel wie von Dir vermutet zwar viele auf Bézierkurven basierende Standard-Formen wie Rechteck, Oval, Viertelkreis, und so weiter, jedoch kann bei diesen über Excel-VBA nur auf den Radius der Ecken, auf die Start- und Endpunkte der Bézierkurven, aber nicht direkt auf deren Führungspunkte zugegriffen werden (anders als in PowerPoint-VBA). In Excel können Führungspunkte von Standardformen tatsächlich nur manuell geändert werden.
Wenn Start- oder Endpunkte einer Standard-Form gelöscht werden (beispielsweise um ein Oval zu halbieren), entsteht intern eine hybride Form, bei der die geänderten Punkte Teil einer Freiform werden, während die beibehaltenen Punkte weiterhin den Beschränkungen der ursprünglichen Standard-Form unterliegen. Es ist daher nicht empfehlenswert eine Standardform zu verwenden, wenn diese anschließend programmatisch nicht nur in Höhe und Breite, oder bezüglich des Radius der Ecken, sondern in deren generellen Form geändert werden soll.
In solchen Fällen ist es besser, direkt eine Freiform mit den benötigten Eigenschaften zu erstellen, da dann auch die Führungspunkte über VBA kontrolliert werden können.

Was hatte meine Frage nun damit zu tun?
Die Grundform für das geplante Steuerelement ist ein gerundetes Rechteck, das mit VBA als Standard-Form erzeugt werden kann (oder eben manuell über die Werkzeugleiste).
Beispiel-Code:

Sub CreateRoundedRectangle ()
Dim ws As Worksheet
Dim shp As Shape
Set ws = ThisWorkbook.Sheets(1) ' Ausgabe im ersten Excel-Tabellenblatt
Set shp = ws.Shapes.AddShape(MsoAutoShapeType.msoShapeRoundedRectangle, 10,10,150,300)
End Sub

Wenn die Höhe dieser Standard-Form nun verkleinert wird, versucht die Rendering-Engine von Excel die gerundeten Ecken so lange wie möglich aufrecht zu erhalten.
Das hat für das genannte Vorhaben jedoch zwei Nachteile:
1. Wenn der Füllstand bei etwa 98 Prozent liegt, soll die obere Rundung wie bei einer Flüssigkeit zunehmend in eine Ecke übergehen. (Das war das Problem hier, wie im dritten Beispiel-Bild "Füllstand_3.png")
2. Wenn der Füllstand bei etwa 2 Prozent liegt, entsteht eine fast schon rechteckige Form in voller Breite. (Das muss ich jetzt noch ableiten)

Deshalb erstelle ich zunächst eine Standard-Form wie im obenstehenden Beispiel, nutze deren Bemaßungs-Komfort, aber wandle diese dann in eine Freiform, die wiederum alle denkbaren Konturänderungen erlaubt.

Füllstand_1
Füllstand_2
Füllstand_3
Antwort
Roman-22

Roman-22

22:20 Uhr, 11.04.2025

Antworten
Ah, jetzt wird der Hintergrund klarer. Danke für die ausführlichen Erläuterungen.
Dass es solche Unterschiede in VBA zwischen Excel und anderen Programmen aus dem M$ Office Paket we PP gibt, war nir nicht bewusst. Allerdings ist meine Erfahrung da auch sehr gering.
Dann noch weiterhin gutes Gelingen!