0

This question furthers a previous question. In that question valter provided me with a very good answer that used two lines of code to perform all the math. I'm hoping he would like to try his hand at this problem also. Even though it looks similar I have not been able to get any math to work.

I need to now draw exactly the same annular sectors accept that the OuterRadius in that question becomes a Rectangle. I include this image to explain what I mean. You will notice the red line that represents the Rectangle plugged into the function. The image is actually a screen capture of what the code I tried up to now produces. Naturally most them are is wrong, but the top left annular sector for instance is correct. All the annular sectors should terminate on the rectangle edge, whilst compensating for the gap. Corner cases would probably need 3 points.

I have decided to post the code I have so far, so the puritans must please close their eyes, because it is pretty brutal:

<Extension()> Friend Sub AddAnnularSector(
    ByVal aGraphicsPath As GraphicsPath,
    ByVal aCenterPoint As PointR,
    ByVal aInnerRadius As Double,
    ByVal aOuterRectangle As RectangleF,
    ByVal aStartAngle As Double,
    ByVal aSweepAngle As Double,
    ByVal aStartGap As Double,
    ByVal aEndGap As Double)
    'Declare local variables...
    Dim tInnerStartOffset As Double = (Math.Asin(aStartGap / aInnerRadius) * 180.0R) / Math.PI
    Dim tInnerEndOffset As Double = (Math.Asin(aEndGap / aInnerRadius) * 180.0R) / Math.PI
    Dim tTestAngle1 As Double = aStartAngle + aSweepAngle - tInnerEndOffset
    If tTestAngle1 > 360.0R Then tTestAngle1 -= 360.0R
    If tTestAngle1 > 270.0R Then tTestAngle1 = 360.0R - tTestAngle1
    If tTestAngle1 > 180.0R Then tTestAngle1 -= 180.0R
    If tTestAngle1 > 90.0R Then tTestAngle1 = 180.0R - tTestAngle1
    Dim tOuterEndLength As Double = (Math.Min(aOuterRectangle.Width, aOuterRectangle.Height) / 2) / Math.Sin(tTestAngle1.ToRadians)
    Dim tTestAngle2 As Double = aStartAngle + tInnerStartOffset
    If tTestAngle2 > 360.0R Then tTestAngle2 -= 360.0R
    If tTestAngle2 > 270.0R Then tTestAngle2 = 360.0R - tTestAngle2
    If tTestAngle2 > 180.0R Then tTestAngle2 -= 180.0R
    If tTestAngle2 > 90.0R Then tTestAngle2 = 180.0R - tTestAngle2
    Dim tOuterStartLength As Double = (Math.Min(aOuterRectangle.Width, aOuterRectangle.Height) / 2) / Math.Sin(tTestAngle2.ToRadians)
    'Add the annular sector to the figure...
    aGraphicsPath.StartFigure()
    aGraphicsPath.AddArc(CSng(aCenterPoint.X - aInnerRadius), CSng(aCenterPoint.Y - aInnerRadius), CSng(aInnerRadius * 2.0R), CSng(aInnerRadius * 2.0R), CSng(aStartAngle + tInnerStartOffset), CSng(aSweepAngle - (tInnerStartOffset + tInnerEndOffset)))
    aGraphicsPath.AddLines(New PointF() {
        New PointF(CSng((aCenterPoint.X) + (Math.Cos((aStartAngle + aSweepAngle - tInnerEndOffset).ToRadians) * tOuterEndLength)), CSng((aCenterPoint.Y) + (Math.Sin((aStartAngle + aSweepAngle - tInnerEndOffset).ToRadians) * tOuterEndLength))),
        New PointF(CSng((aCenterPoint.X) + (Math.Cos((aStartAngle + tInnerStartOffset).ToRadians) * tOuterStartLength)), CSng((aCenterPoint.Y) + (Math.Sin((aStartAngle + tInnerStartOffset).ToRadians) * tOuterStartLength)))
    })
    aGraphicsPath.CloseFigure()
    Return
End Sub

You will notice how OuterRadius of the previous question changed to aOuterRectangle. Also the inner arc is drawn exactly like the previous outer arc was drawn.

EDIT 1: Just chopped the code off a bit to make it more legible.

EDIT 2: Here is an image of what is actually required unlike the above image that just shows the current result.

  • Light Cyan - Figure that will actually be added to the GraphicsPath.
  • Dark Cyan - The part of the figure that is cut away by the OuterRectangle.
  • Green - The OuterRectangle's dimensions including its center point.
  • Yellow - The InnerRadius's dimensions including its center point.
  • Pink - Figure added in a previous call to the function with different inputs just for reference.

EDIT 3: This image shows what I thought would be a quick mathematical solution.

Thanks

drifter

Community
  • 1
  • 1
drifter
  • 5
  • 4

1 Answers1

0

For wdthRect = 250, hgtRect = 200, innerR = 65, startA = 280.0, angle = 30.0, gap = 10.0R

enter image description here

Private Sub DrawAnnular2(ByVal pntC As Point, ByVal wdthRect As Integer, ByVal hgtRect As Integer, ByVal innerR As Integer, ByVal startA As Single, ByVal angle As Single, ByVal gap As Double)
    Dim g As Graphics
    Dim pth As New GraphicsPath
    Dim pthRct As New GraphicsPath
    Dim pthCrclIn As New GraphicsPath
    Dim pthCrclOut As New GraphicsPath
    Dim fe, theta, dbl As Double
    Dim outerR, wdth As Integer
    Dim rect As Rectangle

    wdth = Math.Min(wdthRect, hgtRect)

    outerR = CInt(Math.Sqrt(2.0R * (CDbl(wdth) / 2.0R) * (CDbl(wdth) / 2.0R))) 'πυθαγόρειο θεώρημα

    rect.X = CInt(CDbl(pntC.X) - CDbl(wdth) / 2.0R)
    rect.Y = CInt(CDbl(pntC.Y) - CDbl(wdth) / 2.0R)
    rect.Width = wdth
    rect.Height = wdth
    pthCrclOut.AddEllipse(pntC.X - outerR, pntC.Y - outerR, 2 * outerR, 2 * outerR)
    pthCrclIn.AddEllipse(rect)
    pthRct.AddRectangle(rect)

    '////// The same as annular 1 //////////////////////////////////////////////////
    g = Me.CreateGraphics
    g.SmoothingMode = SmoothingMode.AntiAlias

    gap /= 2.0R

    dbl = gap / CDbl(outerR)

    theta = Math.Asin(dbl) * 180.0R / Math.PI

    fe = theta

    pth.AddArc(pntC.X - outerR, pntC.Y - outerR, 2 * outerR, 2 * outerR, startA + CSng(fe), angle - CSng(2.0R * fe)) 'Outer

    dbl = gap / CDbl(innerR)

    theta = Math.Asin(dbl) * 180.0R / Math.PI

    fe = theta

    pth.AddArc(pntC.X - innerR, pntC.Y - innerR, 2 * innerR, 2 * innerR, startA + angle - CSng(fe), -(angle - CSng(2.0R * fe))) 'Inner

    '////////////////////////////////////////////////////////////

    Dim Reg1 As New Region(pth)

    Dim Reg2 As New Region(pthRct)

    Reg2.Intersect(Reg1)

    g.FillRegion(Brushes.Aqua, Reg2) 'This is the actual annular 2.
    g.DrawPath(Pens.Green, pthCrclIn)
    g.DrawPath(Pens.Green, pthCrclOut)
    g.DrawPath(Pens.Red, pthRct)

    Reg1.Dispose()
    Reg2.Dispose()
    pthRct.Dispose()
    pthCrclOut.Dispose()
    pthCrclIn.Dispose()
    pth.Dispose()
    g.Dispose()
End Sub

This line of code:

Reg2.Intersect(Reg1)

is actually the intersect between the blue and the red

enter image description here

EDIT

Private Function DrawAnnular2(ByVal pntC As Point, ByVal wdthRect As Integer, ByVal hgtRect As Integer, ByVal innerR As Integer, ByVal startA As Single, ByVal angle As Single, ByVal gap As Double) As GraphicsPath
    Dim g As Graphics
    Dim pth As New GraphicsPath
    Dim pthRct As New GraphicsPath
    Dim pthFinal As New GraphicsPath
    Dim fe, theta, dbl As Double
    Dim outerR, wdth As Integer
    Dim rect As Rectangle
    Dim lst1 As New List(Of Integer)
    Dim lst2 As New List(Of Integer)
    Dim lst3 As New List(Of Integer)
    Dim lst4 As New List(Of Integer)
    Dim i As Integer
    Dim lstBl(3) As Boolean
    Dim position As Integer

    lstBl(0) = False
    lstBl(1) = False
    lstBl(2) = False
    lstBl(3) = False

    wdth = Math.Min(wdthRect, hgtRect)

    outerR = CInt(Math.Sqrt(2.0R * (CDbl(wdth) / 2.0R) * (CDbl(wdth) / 2.0R))) 'πυθαγόρειο θεώρημα

    rect.X = CInt(CDbl(pntC.X) - CDbl(wdth) / 2.0R)
    rect.Y = CInt(CDbl(pntC.Y) - CDbl(wdth) / 2.0R)
    rect.Width = wdth
    rect.Height = wdth
    pthRct.AddRectangle(rect)

    '////////////////////////////////////////////////////////
    g = Me.CreateGraphics
    g.SmoothingMode = SmoothingMode.AntiAlias

    gap /= 2.0R

    dbl = gap / CDbl(outerR)

    theta = Math.Asin(dbl) * 180.0R / Math.PI

    fe = theta

    If CDbl(angle) - 2.0R * fe >= 360.0R Then
        pthFinal.AddEllipse(pntC.X - innerR, pntC.Y - innerR, 2 * innerR, 2 * innerR)
        pthFinal.AddRectangle(rect)

        g.FillPath(Brushes.Aqua, pthFinal)
        g.DrawPath(Pens.Red, pthRct)

        pthRct.Dispose()
        pth.Dispose()
        g.Dispose()

        Return pthFinal
    End If

    pth.AddArc(pntC.X - outerR, pntC.Y - outerR, 2 * outerR, 2 * outerR, startA + CSng(fe), angle - CSng(2.0R * fe)) 'Outer

    dbl = gap / CDbl(innerR)

    theta = Math.Asin(dbl) * 180.0R / Math.PI

    fe = theta

    pth.AddArc(pntC.X - innerR, pntC.Y - innerR, 2 * innerR, 2 * innerR, startA + angle - CSng(fe), -(angle - CSng(2.0R * fe))) 'Inner

    '////////////////////////////////////////////////////////////


    For i = rect.X To rect.X + wdth
        If pth.IsVisible(i, rect.Y) Then
            If lst1.Count <> 0 Then
                If i <> lst1(lst1.Count - 1) + 1 Then
                    lstBl(0) = True
                    position = lst1.Count
                End If
            End If

            lst1.Add(i)
        End If
    Next

    For i = rect.Y To rect.Y + wdth
        If pth.IsVisible(rect.X + wdth, i) Then
            If lst2.Count <> 0 Then
                If i <> lst2(lst2.Count - 1) + 1 Then
                    lstBl(1) = True
                    position = lst2.Count
                End If
            End If

            lst2.Add(i)
        End If
    Next

    For i = rect.X To rect.X + wdth
        If pth.IsVisible(i, rect.Y + wdth) Then
            If lst3.Count <> 0 Then
                If i <> lst3(lst3.Count - 1) + 1 Then
                    lstBl(2) = True
                    position = lst3.Count
                End If
            End If

            lst3.Add(i)
        End If
    Next

    For i = rect.Y To rect.Y + wdth
        If pth.IsVisible(rect.X, i) Then
            If lst4.Count <> 0 Then
                If i <> lst4(lst4.Count - 1) + 1 Then
                    lstBl(3) = True
                    position = lst4.Count
                End If
            End If

            lst4.Add(i)
        End If
    Next


    'If lstBl(0) = True Or lstBl(1) = True Or lstBl(2) = True Or lstBl(3) = True Then
        'It is a rare case that i have to work on, when angle is too large
        'MsgBox(lstBl(0).ToString + " " + lstBl(1).ToString + " " + lstBl(2).ToString + " " + lstBl(3).ToString + " ")

        'Application.Exit()
    'End If

    'TextBox1.Text = lst1.Count.ToString + " " + lst2.Count.ToString + " " + lst3.Count.ToString + " " + " " + lst4.Count.ToString

    pthFinal.AddArc(pntC.X - innerR, pntC.Y - innerR, 2 * innerR, 2 * innerR, startA + angle - CSng(fe), -(angle - CSng(2.0R * fe))) 'Inner

    If CDbl(startA) + fe >= 225.0R And CDbl(startA) + fe <= 315.0R Then '1
        If lst1.Count <> 0 Then
            If lstBl(0) = True Then
                pthFinal.AddLine(lst1(position), rect.Y, lst1(lst1.Count - 1), rect.Y)
            Else
                pthFinal.AddLine(lst1(0), rect.Y, lst1(lst1.Count - 1), rect.Y)
            End If
        End If
        If lst2.Count <> 0 Then
            pthFinal.AddLine(lst1(lst1.Count - 1), rect.Y, rect.X + wdth, lst2(lst2.Count - 1))
        End If
        If lst3.Count <> 0 Then
            pthFinal.AddLine(rect.X + wdth, lst2(lst2.Count - 1), lst3(0), rect.Y + wdth)
        End If
        If lst4.Count <> 0 Then
            pthFinal.AddLine(lst3(0), rect.Y + wdth, rect.X, lst4(0))
        End If
        If lstBl(0) = True Then
            pthFinal.AddLine(rect.X, lst4(0), lst1(position - 1), rect.Y)
        End If
    ElseIf (CDbl(startA) + fe > 315.0R And CDbl(startA) + fe <= 360.0R) Or _
           (CDbl(startA) + fe >= 0.0R And CDbl(startA) + fe <= 45.0R) Then '2

        If lst2.Count <> 0 Then
            If lstBl(1) = True Then
                pthFinal.AddLine(rect.X + wdth, lst2(position), rect.X + wdth, lst2(lst2.Count - 1))
            Else
                pthFinal.AddLine(rect.X + wdth, lst2(0), rect.X + wdth, lst2(lst2.Count - 1))
            End If
        End If
        If lst3.Count <> 0 Then
            pthFinal.AddLine(rect.X + wdth, lst2(lst2.Count - 1), lst3(0), rect.Y + wdth)
        End If
        If lst4.Count <> 0 Then
            pthFinal.AddLine(lst3(0), rect.Y + wdth, rect.X, lst4(0))
        End If
        If lst1.Count <> 0 Then
            pthFinal.AddLine(rect.X, lst4(0), lst1(lst1.Count - 1), rect.Y)
        End If
        If lstBl(1) = True Then
            pthFinal.AddLine(lst1(lst1.Count - 1), rect.Y, rect.X + wdth, lst2(position - 1))
        End If
    ElseIf CDbl(startA) + fe > 45.0R And CDbl(startA) + fe <= 135.0R Then '3
        If lst3.Count <> 0 Then
            If lstBl(2) = True Then
                pthFinal.AddLine(lst3(position - 1), rect.Y + wdth, lst3(0), rect.Y + wdth)
            Else
                pthFinal.AddLine(lst3(lst3.Count - 1), rect.Y + wdth, lst3(0), rect.Y + wdth)
            End If
        End If
        If lst4.Count <> 0 Then
            pthFinal.AddLine(lst3(0), rect.Y + wdth, rect.X, lst3(0))
        End If
        If lst1.Count <> 0 Then
            pthFinal.AddLine(rect.X, lst3(0), lst1(lst1.Count - 1), rect.Y)
        End If
        If lst2.Count <> 0 Then
            pthFinal.AddLine(lst1(lst1.Count - 1), rect.Y, rect.X + wdth, lst2(lst2.Count - 1))
        End If
        If lstBl(2) = True Then
            pthFinal.AddLine(rect.X + wdth, lst2(lst2.Count - 1), lst3(position), rect.Y + wdth)
        End If
    Else '4
        If lst4.Count <> 0 Then
            If lstBl(3) = True Then
                pthFinal.AddLine(rect.X, lst4(position - 1), rect.X, lst4(0))
            Else
                pthFinal.AddLine(rect.X, lst4(lst4.Count - 1), rect.X, lst4(0))
            End If
        End If
        If lst1.Count <> 0 Then
            pthFinal.AddLine(rect.X, lst4(0), lst1(lst1.Count - 1), rect.Y)
        End If
        If lst2.Count <> 0 Then
            pthFinal.AddLine(lst1(lst1.Count - 1), rect.Y, rect.X + wdth, lst2(lst2.Count - 1))
        End If
        If lst3.Count <> 0 Then
            pthFinal.AddLine(rect.X + wdth, lst2(lst2.Count - 1), lst3(0), rect.Y + wdth)
        End If
        If lstBl(3) = True Then
            pthFinal.AddLine(lst3(0), rect.Y + wdth, rect.X, lst4(position))
        End If
    End If


    'g.FillPath(Brushes.Blue, pth)
    g.FillPath(Brushes.Aqua, pthFinal)
    g.DrawPath(Pens.Red, pthRct)

    pthRct.Dispose()
    pth.Dispose()
    g.Dispose()

    Return pthFinal
End Function

Your values:

enter image description here

John Saunders
  • 160,644
  • 26
  • 247
  • 397
  • That means you are painting directly. I need to put this in a path that gets delivered somewhere else later, without any `Region` information. If you look at **EDIT2** of the your answer to my previous [question](http://stackoverflow.com/questions/25886948/how-to-draw-very-specific-annular-sectors-using-visual-basic-net-2) you will see that we had to do the math ourselves because it goes into a path. The path figure must be completely self sustained with all unnecessary bits removed by the function that creates it. Might be impossible I fear... – drifter Sep 17 '14 at 14:18
  • As far as I know a `Region` does not support Anti-Alias, so it would paint very ugly later, and there is no way to convert the region back to a `GraphicsPath`. I will play with the code a bit, but hope is neigh. – drifter Sep 17 '14 at 14:40
  • I thought the same thing, but as you can see from my code I almost got it to work. The example image was drawn using paths created by that function. I am just not sure my mathematical wisdom can take it any further. I don't even know why it does not work properly. If you don't feel like it I will understand 100%. I will post another image to show what I actually need, so you will see how much I have already dulled it down. – drifter Sep 17 '14 at 15:10
  • I pasted your code into my project, but it does not draw anything (yet). To use `IsVisible` is something I would never have thought about. I am going to remove your drawing code to see if just the rest won't work. Will report back first chance I get. Thanks for the effort. – drifter Sep 17 '14 at 19:25
  • `.DrawAnnular2(New Point(300I, 300I), 600I, 600I, 185I, 185.0F, 15.0F, 5.0R)` – drifter Sep 17 '14 at 19:48
  • I was being vague. I should have written "It probably draws just fine, since I can see your test code but in order to use it I pasted it in a module as an `` method and removed all references to stuff like `Graphics` and `Me` etc. since they would give errors. Now it does not work.". I will try to get it working again and report back with bugs (if any) I may find. – drifter Sep 17 '14 at 20:21
  • You probably know everything I am about to say, but I just have to know. After the previous solution you gave me I was so sure you would use Pythagoras' Theory in combination with the unit circle to solve the problem. Why did you decide against it? (See "Edit 3" above for an example of what I mean). – drifter Sep 17 '14 at 20:53
  • @drifter This solution doesn't work because the parallel line (gap) doesn't pass from the center of inner circle or the rectangle. In order for it to work you need more things and i didn't want to make it more complicated than it is. It may seem that my solution is very complicated but in actuallity is very simple. I just calculate the intersect points of *path* with each side of rectangle (the for four loops), add them to the lists(lst1 upperside lst2 right side etc) and then add the lines. I can simplified the code even more but it works like this so i left it as is. – γηράσκω δ' αεί πολλά διδασκόμε Sep 17 '14 at 21:15
  • @drifter The *lstBl()* is to see if the start angle and the end angle intersect with the same side of rectangle so to add another line to *pthFinal* – γηράσκω δ' αεί πολλά διδασκόμε Sep 17 '14 at 21:19
  • @drifter The *If elseIf ...* is to check in which side of rectangle the start angle intersects with. See the numbers *1, 2, 3, 4*. *1 for upper side, 2 for right side, 3 for down side and 4 for left side* – γηράσκω δ' αεί πολλά διδασκόμε Sep 17 '14 at 21:26
  • All is well that ends well... I got it to work and it made short change of the initial angles I threw at it. Thanks for the great help. – drifter Sep 17 '14 at 22:28