I'm reading the following page about steering AI:
http://rocketmandevelopment.com/blog/steering-behaviors-wander/
At the bottom is some code which I'm trying to covert for use in VB.NET + XNA
My code looks like this:
Sub AI()
Dim circleRadius As Single = 6.0F
Dim wanderAngle As Single = 0.0F
Dim wanderChange As Single = 1.0F
Dim enemySpeed As Single = 0.3F
Dim enemyPosistion As Vector2 = (1,1)
Dim circleMiddle As Vector2 = enemyPosistion
circleMiddle.Normalize()
circleMiddle = Vector2.Multiply(circleMiddle, circleRadius)
Dim wanderForce As New Vector2
wanderForce = Vector2.Normalize(wanderForce) * 3 ' Set length of vector
wanderForce = AngleToVector(wanderAngle)
Randomize()
wanderAngle += Rnd() * wanderChange - wanderChange * 0.5
Dim force As New Vector2
force = Vector2.Add(circleMiddle, wanderForce)
enemyPosistion += force * enemySpeed
End Sub
Private Function AngleToVector(angle As Single) As Vector2
Return New Vector2(CSng(Math.Sin(angle)), -CSng(Math.Cos(angle)))
End Function
I realised I made a simple mistake by setting enemyPosistion to Vector2.Zero, instead I set it to (1,1) and it makes the enemy fly up and to the right. I have included a video:
https://www.youtube.com/watch?v=UZubNaEA9W0
This is more along the lines of what it should do:
https://www.youtube.com/watch?v=1wfgPCMdW2U
Can anybody tell me what I'm doing wrong?
You need to preserve some state across frames. At least the wanderAngle and a velocity vector. Call Randomize() only once at the beginning of the game. circleMiddle should be velocity * circleRadius. velocity should be updated at the end with circleMiddle + wanderForce and enemyPosition += velocity * enemySpeed. Honestly, this whole code looks a bit strange, mixing up some physical concepts. Forces affect a body's acceleration (together with mass), acceleration affects its velocity (together with a time step) and the velocity affects its position (together with a time step). - Nico Schertler
Related
Please bear with me I´m a newbie in programming and I´m trying to learn how to rotate a 3D point (XYZ) around 0,0,0 and later I´ll try to improve my code to allow rotation around an arbitrary point (XYZ).
I´m starting with VB and after extensive searches here and in the Web, I could not find an explanation for my problem. I´m an almost 40 years old trying to learn math and programming, so please bear with me because it will take time for me to digest all the math side for these problems.
Basically, I´m trying to write an algorithm to rotate a 3D point, however, while it seems that with some angles my code works, with some others I just get weird funky numbers that are probable correct in some aspect, but I can´t find the flaw in the code. I´ve been looking into this for days and tried multiple approaches, but I´m just not being able to spot the error.
This is the UI for my little app. The original coordinates are entered in the top of the form, and in the bottom of the form I show the rotated coordinates.
Notice that in the image below, a simple rotation of a coordinate of Y10.0 around Z axis by 90 degrees return a correct X value (-10), but Y shows a funky number (6.1230...)... However if I change the rotation angle around Z to 45, the results seems to be correct...
I don´t know what I´m doing wrong to get this weird Y. Because of this error, I´m not trusting in the results of this algorithm at all but I´m currently in a blindspot...
This is the code of the calculate button:
Private Sub BtnCompute_Click(sender As Object, e As EventArgs) Handles BtnCompute.Click
'Capture the values from the text boxes and parse then to doubles
ValidateAllFieldsWithDoubleValues()
'Rotate the coordinates
RotateXYZCoordinates(dblOriginalCoordX, dblOriginalCoordY, dblOriginalCoordZ, dblCurrentRotationAroundX, dblCurrentRotationAroundY, dblCurrentRotationAroundZ)
'Update the text boxes for the rotated coordinates for XYZ
txtResultX.Text = dblResultX.ToString
txtResultY.Text = dblResultY.ToString
txtResultZ.Text = dblResultZ.ToString
End Sub
And this is the code of the function that calculates the rotations:
Private Function RotateXYZCoordinates(ByVal XCoord As Double, ByVal YCoord As Double, ByVal ZCoord As Double, ByVal Pitch As Double, ByVal Roll As Double, ByVal Yaw As Double)
'X Rotation
Dim RadPitch As Double = 0
Dim CosPitch As Double = 0
Dim SinPitch As Double = 0
Dim XRotatedAroundX As Double = 0
Dim YRotatedAroundX As Double = 0
Dim ZRotatedAroundX As Double = 0
RadPitch = Pitch * Math.PI / 180
CosPitch = Math.Cos(RadPitch)
SinPitch = Math.Sin(RadPitch)
XRotatedAroundX = XCoord
YRotatedAroundX = YCoord * CosPitch - ZCoord * SinPitch
ZRotatedAroundX = YCoord * SinPitch + ZCoord * CosPitch
'Y Rotation
Dim RadRoll As Double = 0
Dim CosRoll As Double = 0
Dim SinRoll As Double = 0
Dim XRotatedAroundY As Double = 0
Dim YRotatedAroundY As Double = 0
Dim ZRotatedAroundY As Double = 0
RadRoll = Roll * Math.PI / 180
CosRoll = Math.Cos(RadRoll)
SinRoll = Math.Sin(RadRoll)
XRotatedAroundY = ZRotatedAroundX * CosRoll - XRotatedAroundX * SinRoll
YRotatedAroundY = YRotatedAroundX
ZRotatedAroundY = ZRotatedAroundX * SinRoll + XRotatedAroundX * CosRoll
'Z Rotation
Dim RadYaw As Double = 0
Dim CosYaw As Double = 0
Dim SinYaw As Double = 0
Dim XRotatedAroundZ As Double = 0
Dim YRotatedAroundZ As Double = 0
Dim ZRotatedAroundZ As Double = 0
RadYaw = Yaw * Math.PI / 180
CosYaw = Math.Cos(RadYaw)
SinYaw = Math.Sin(RadYaw)
XRotatedAroundZ = XRotatedAroundY * CosYaw - YRotatedAroundY * SinYaw
YRotatedAroundZ = XRotatedAroundY * SinYaw + YRotatedAroundY * CosYaw
ZRotatedAroundZ = ZRotatedAroundY
'Final result
dblResultX = XRotatedAroundZ
dblResultY = YRotatedAroundZ
dblResultZ = ZRotatedAroundZ
Return Nothing
End Function
I know this is not an elegant code but it is what I can code for now... I´d appreciate if someone could take a look at this and point me to the source of error... I´ve been watching videos and did an extensive search in this website before I posted... But it seems some things are still very advanced to me for now... I´m not lazy and I´m willing to learn if someone point me towards something I could digest for now...
If someone could share a hint about how to make this rotate function to support rotation around a point other than 0,0,0 I´d appreciate.
Thank you,
Daniel
The answer is correct. Due to double precision math and a 90 degree rotation there is a limit to the accuracy. The answer is really 6.12303176911189E-16 or .000000000000000612303176911189. Round the number off to a realistic value of decimal points. This is also why 1+1 is not equal to 2 but 1.999999999999999999999999999999 in floating point math.
I have this code I sorta put together from a few different sites.
It works perfect at creating the bitangent... but for what ever reason. the tangent is thrashed.. In other words and to make this more clear, the code is generating BAD tangents!!
The tangent is usually pointing down, kinda opposite the normal. I'm expecting it to be aligned with the V axis of the texture coordinate..
The strange thing is, I use the cross of the normal and tangent to get the bitangent, which is fine.
I have tried flipping the coordinates, the UVs.. just about everything..
Im not great in math so.. can some one take a look at my code and see if it looks "legit"? Thank you!
Private Sub ComputeTangentBasis(ByRef p0 As vertex_data, ByRef p1 As vertex_data, ByRef p2 As vertex_data)
Dim tangent, bitangent As Vector3D
Dim n As Vector3D
n.X = p0.nx
n.Y = p0.ny
n.Z = p0.nz
'convert to vector3d type... they are WAY easier to do complex math with!!
Dim v0 = convert_vector3d(p0)
Dim v1 = convert_vector3d(p1)
Dim v2 = convert_vector3d(p2)
Dim edge1 = v1 - v2
Dim edge2 = v2 - v0
Dim deltaU1 = (p1.u - p0.u) * -0.1
Dim deltaU2 = (p2.u - p0.u) * -0.1
Dim deltaV1 = (p1.v - p0.v) * -0.1
Dim deltaV2 = (p2.v - p0.v) * -0.1
Dim dividend = (deltaU1 * deltaV2) - (deltaU2 - deltaV1)
Dim f As Double
If dividend = 0.0 Then
f = 1.0
End If
tangent.X = f * ((deltaV2 * edge1.X) - (deltaV1 * edge2.X))
tangent.Y = f * ((deltaV2 * edge1.Y) - (deltaV1 * edge2.Y))
tangent.Z = f * ((deltaV2 * edge1.Z) - (deltaV1 * edge2.Z))
bitangent = Vector3D.CrossProduct(tangent, n)
'
p0.t.x = tangent.X
p0.t.y = tangent.Y
p0.t.z = tangent.Z
p0.bt.x = bitangent.X
p0.bt.y = bitangent.Y
p0.bt.z = bitangent.Z
'
p1.t.x = tangent.X
p1.t.y = tangent.Y
p1.t.z = tangent.Z
p1.bt.x = bitangent.X
p1.bt.y = bitangent.Y
p1.bt.z = bitangent.Z
'
p2.t.x = tangent.X
p2.t.y = tangent.Y
p2.t.z = tangent.Z
p2.bt.x = bitangent.X
p2.bt.y = bitangent.Y
p2.bt.z = bitangent.Z
End Sub
Here's a pic of the problem.. Norms are red.. Tangents Green... BiTangents.. blueish.
You will likely need to recalculate the tangent from cross-product of the normal and bitangent. This ensures that the normal, tangent and bitangent are orthagonal to one another.
In most implementations I've seen, the first calculation for a perpendicular axis to the normal is a placeholder as there are an infinite number of possible axes to choose from, and as such requires it to be recalculated to be orthagonal.
In your code you don't take dividend into the calculus, except for the case is 0.
Any two points in a triangle form a tangent or a bitangent. There are infinite possibilities. If you want to align T and B with some texture coordinates you need more maths.
With little effort you would have found this: How to calculate Tangent and Binormal?
Ok... I found 2 problems..
Dim edge1 = v1 - v2 should be Dim edge1 = v1 - v0
I looked in to what Ripi2 commented about.. I watched the video I got this from over again.. I messed a part where he changed the code.. He removed dividend and the if statement and replaced it with this line:
Dim f As Single = 1.0! / ((deltaU1 * deltaV2) - (deltaU2 * deltaV1))
Now it creates prefect Tangents and BiTangents.
I added normalization using these 2 lines.
tangent /= tangent.Length
bitangent /= bitangent.Length
I hope this helps anyone out there.
I assumed that cropping an image would be an extremely easy thing to do from .net. But no matter what I try I just cannot seem to get the thing to work.
The documentation is somewhat vague -
'The first parameter is an array of four coordinates that mark the portion remaining after cropping'
That could mean an array of four numbers, or it could mean an array of four arrays of two numbers (a coordinate after all consists of two numbers). the 'portion remaining after cropping' I take to mean 'the portion of the image designated to remain after cropping'.
Since the select function takes an array of coordinate arrays -- {{x1,y1}, y2, y2}, {x3,y3}, {x4, y4}} -- I had hoped crop would work the same way. No dice.
Next, I tried the really simple approach, assume that 'left, top, right, bottom' really mean just that. So, I plugged in perfectly reasonable values and ... no dice.
In every case, PS throws a dialog box saying ' Could not complete the command because the affected area is empty or does not overlap the canvas'.
Here is a code snippet:
Dim PSDapp
PSDapp = CreateObject("Photoshop.Application")
Dim psarray As Object = {20, 20, 120, 120}
Dim PSDcurrentDoc
PSDapp.preferences.rulerUnits = 1
PSDcurrentDoc = PSDapp.open("c:\cat.bmp")
PSDapp.activeDocument = PSDapp.documents.item(1)
PSDcurrentDoc.crop(psarray)
What is even more strange is that if I take the above code and port it to a script, it runs just fine. Can someone (anyone!) please please post a minimal working example of using the crop feature using COM (not scripting)?
I've never used Photoshop, but an array of coordinates could be written like this:
Dim psarray() As Point = {
New Point(20, 20),
New Point(120, 20),
New Point(120, 120),
New Point(20, 120)
}
PSDcurrentDoc.crop(psarray)
So you tried something like this already?
Dim psarray() As Integer = {20, 20, 120, 120}
PSDcurrentDoc.crop(psarray)
If that doesn't work, try "pinning" it:
Dim psarray() As Integer = {20, 20, 120, 120}
Dim gch As System.Runtime.InteropServices.GCHandle
gch = System.Runtime.InteropServices.GCHandle.Alloc(psarray, Runtime.InteropServices.GCHandleType.Pinned)
PSDcurrentDoc.crop(gch.AddrOfPinnedObject)
gch.Free()
Solved, with a work-around.
I used a selection as a work-around. The code is longer than it really should have to be since I must first make a selection, but it does work.
Below is the full working subroutine connected to a button. I hope it is of use to somebody that might face this issue as well.
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim x As Integer = 100 ' The x-coordinate of the upper-left corner (pixel units).
Dim y As Integer = 100 ' The y-coordinate of the upper-left corner (pixel units).
Dim w As Integer = 200 ' The width of the selection/crop (pixel units).
Dim h As Integer = 200 ' The height of the selection/crop (pixel units).
Dim psArray1() As Object = {x, y}
Dim psArray2() As Object = {x, y + h}
Dim psArray3() As Object = {x + w, y + h}
Dim psArray4() As Object = {x + w, y}
Dim psArray() As Object = {psArray1, psArray2, psArray3, psArray4} ' A concatenated object consisting of an array of coordinates.
Dim PSDapp As Object = CreateObject("Photoshop.Application")
Dim PSDcurrentDoc As Object = PSDapp.open("c:\cat.bmp") ' Could be any document of course.
PSDapp.preferences.rulerUnits = 1
PSDcurrentDoc.selection.select(psArray)
Dim selectArray As Object = PSDcurrentDoc.selection.bounds
PSDcurrentDoc = PSDcurrentDoc.crop(selectArray) ' This is key. PSDcurrentDoc.crop is read-only, so it must be assigned.
End Sub
I need to add a half inch of white space at the bottom of an image and the draw a string to the bottom left and bottom right (within the newly added white space). Everything seems to work fine but the font sometime appears way too small or too large.
I think I need to somehow scale the drawstring font to the size of the image? I have exhausted myself trying to figure this out... Please help!!
See code below ----
Imports System.IO
Imports System.Drawing.Graphics
Imports System.Drawing
Imports System.Drawing.Bitmap
Imports System.Drawing.Imaging
Public Class Form1
Dim ofilepath As String = "C:\temp\20141022\TEST0000001.tif"
Dim nfilepath As String = "C:\temp\20141022\new.tif"
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim newbm As New Bitmap(AddBorderAndStamp(Bitmap.FromFile(ofilepath), Color.White, 50, Path.GetFileNameWithoutExtension(ofilepath), "CONFIDENTIAL"))
newbm.Save(nfilepath, Imaging.ImageFormat.Tiff)
Me.Close()
End Sub
Public Function AddBorderAndStamp(ByVal bm As Bitmap, ByVal borderColor As System.Drawing.Color, ByVal borderWidthInPixels As Integer, ByVal bates As String, ByVal designation As String) As Bitmap
Dim voffset As Integer = 75
Dim hoffset As Integer = 15
Dim newBitmap As New Bitmap(bm.Width, bm.Height + (borderWidthInPixels * 2))
Dim mfont As Font = New Font("Arial", 32, FontStyle.Bold)
For x As Integer = 0 To newBitmap.Width - 1
For y As Integer = newBitmap.Height - 1 To (newBitmap.Height - 1) - borderWidthInPixels Step -1
newBitmap.SetPixel(x, y, borderColor)
Next
Next
Dim gr As System.Drawing.Graphics = Graphics.FromImage(newBitmap)
gr.Clear(Color.White)
gr.DrawImage(bm, 0, 0, bm.Width, bm.Height)
Dim textSize As SizeF = gr.MeasureString(bates, mfont)
gr.DrawString(bates, mfont, Brushes.Black, bm.Width - textSize.Width - hoffset, newBitmap.Height - voffset)
gr.DrawString(designation, mfont, Brushes.Black, hoffset, newBitmap.Height - voffset)
gr.Dispose()
Return newBitmap
End Function
End Class
When you're about to know the size (width) of your bates string at this line :
Dim textSize As SizeF = gr.MeasureString(bates, mfont)
and before drawing your string on the bitmap, you'll have to compute the scaling to perform on your font size to make sure your text gets shrinked (if too long) or stretched (if too narrow) while not excessively stretched in height-mean to be drawn over the image.
Then, only when you have a valid font size, you draw your text as you do with the following lines
gr.DrawString(bates, mfont, Brushes.Black, bm.Width - textSize.Width - hoffset, newBitmap.Height - voffset)
gr.DrawString(designation, mfont, Brushes.Black, hoffset, newBitmap.Height - voffset)
To know what's the correct font size to use, have a look on this SO Question Page.
I know it's C#, but this is basically what you'll have to do before drawing your bates and designation text. The difference lies in two points :
you have two String to scale in a clip rectangle (bates and designation) instead of one.
you have to take account of a (default) separation between designation on the left, and bates on the right.
But these two points can be easily worked around. This also means that your question could be a duplicate (StackOverflow doesn't recommend multiple variants of the same question as the purpose of SO is to bring you directly to a page asking a question AND its answer - if answered - but not to flood you with dozens copies of the same subject)
But IMHO, the two-string (designation and bates) is enough to consider this as a new question, because the code handling that is not covered by the other topics.
Here is saeed's function converted to vb.Net
Private Function FindFont( _
ByRef rg As Graphics, _
ByVal testString as String, _
ByVal clipRoom As Size, _
ByVal preferedFont As Font) As Font
' You should perform some scale functions...
Dim realSize As SizeF = rg.MeasureString(testString, PreferedFont)
Dim heightScaleRatio As Double = clipRoom.Height / realSize.Height
Dim widthScaleRatio As Double = clipRoom.Width / realSize.Width
Dim scaleRatio As Double
Dim newFontSize As Single ' I'm used to declare everything on top.
If heightScaleRatio < widthScaleRatio Then
scaleRatio = heightScaleRatio
Else
scaleRatio = widthScaleRatio
End If
newFontSize = CSng(preferedFont.Size * ScaleRatio)
Return New Font(preferedFont.FontFamily, newFontSize, preferedFont.Style)
End Function
Credits goes to saeed first for providing the code, but if saeed used code from someone else that I'm not aware of, credits to original author supersede saeed's.
The required parameters for that Function to work are :
rg As Graphics. You already have it, namely rg in your AddBorderAndStamp Function.
testString As String. You also have it, it is simply testString = designation + bates
clipRoom As Size. You don't have that variable yet. You'll have to declare it in your AddBorderAndStamp Function, and use some logic to define its .Width and .Height values.
and preferedFont As Font. You also have that already, which is mfont As Font = New Font("Arial", 32, FontStyle.Bold)
The declarations to add to your AddBorderAndStamp Function are :
Dim clipRoom As Size ' Declare it.
Dim stampSeparation As Integer = 80 ' why 80 ? dunno ! it's an arbitrary value..
stampSeparation is an arbitraty variable that represents the Width IN
PIXELs between designation and bates. Basically, it looks like this :
'< - - - - - - - total Bitmap width - - - - - - - >
'| |designation|_____________|bates| |
'| | | | | ^ Right Image boder
'| | | | ^ Right margin
'| | | ^ Left plot of your bates String
'| | <-------------> Length or Width of stampSeparation
'| ^ Left plot of your designation String
'^ Left image border
Your available room to write text is the sum of
designation.Width
bates.Width
stampSeparation
But because you want designation and bates beeing separated by stampSeparation, stampSeparation must be substracted from the available clip width. So :
clipRoom.Width = newBitmap.Width - (hOffset * 2) - stampSeparation
' newBitmap.Width - 30 - 80
' newBitmap.Width - 110
' CAREFULL !!! clipRoom.Width MUST be positive !!!
' Check your bm is wide enough, say at least 200 pixels Width...
The room you have at the bottom is a sightly different story : Your AddBorderAndStamp Function has one borderWidthInPixels (as Integer) parameter that define the room you add in top/bottom mean. Then you use voffset (as Integer) variable to shift your stamp upward.. That means your available room in terms of heigth at the bottom of your image to plot text in is :
clipRoom.Height = vOffset ' ?
' 75 ?
' (CAREFULL !!! clipRoom.Height MUST be positive !!!)
If I were you, I would dynamically define clipRoom.Height based on a fraction of AddBorderAndStamp and dynamically calculate vOffset after knowing the final heigth of designation and bates heights using MeasureString... But that would be an overkill...
' perhaps one simple logic like the following would suffice
' clipRoom.Height = CInt(vOffset * 4 / 5)
Now you have clipRoom and everything else required to call FindFont(...) Function.
So :
Public Function AddBorderAndStamp(...) As Bitmap
' Dim vOffset ' ...
' Dim hOffset ' ...
' ...
' add the following declarations :
Dim clipRoom As Size
Dim stampSeparation As Integer = 80 ' or 60 as you like but small enough
' ...
' ... your function block code goes here until the following line :
gr.DrawImage(bm, 0, 0, bm.Width, bm.Height)
' Calculate the available clip room...
clipRoom.Width = newBitmap.Width - (hOffset * 2) - stampSeparation
clipRoom.Height = vOffset
' ^^ remember : the logic to handle valid Width and Height is up to you
' I know how to handle that, but you should aswell, or try at least.
' Now, update the size of your font...
mFont = FindFont(rg, designation + bates, clipRoom, mFont)
' then continue with the rest of the code...
Dim textSize As SizeF = gr.MeasureString(bates, mfont)
gr.DrawString(bates, mfont, Brushes.Black, bm.Width - textSize.Width - hoffset, newBitmap.Height - voffset)
gr.DrawString(designation, mfont, Brushes.Black, hoffset, newBitmap.Height - voffset)
gr.Dispose()
Return newBitmap
End Function
by the way :
Hello. You should add [vb.Net] tag to the post and either remove
[image] or [bitmap] since those are synonyms.
It's hard to spot code typos without the formatting. But since you're new on StackOverflow (SO) I don't want to downvote or flag you, but I insist : update the tags of your question.
Remark 1 :
You wrote :
For x As Integer = 0 To newBitmap.Width - 1
For y As Integer = newBitmap.Height - 1 To (newBitmap.Height - 1) - borderWidthInPixels Step -1
newBitmap.SetPixel(x, y, borderColor)
Next
Next
Dim gr As System.Drawing.Graphics = Graphics.FromImage(newBitmap)
gr.Clear(Color.White)
You're changing the color of newBitmap pixel by pixel from the bottom. I'm OK with that but there are better ways to achieve that.
However, you're declaring a Graphics to handle drawings on newBitmap, then you call gr.Clear(Color.White) ???
Isn't that supposed to entirely paint your newBitmap with white, thus, destroing the purpose of your SetPixel loop just above ?
I don't understand. Perhaps you want the function to waste time, so just use
System.Threading.Thread.Sleep(X) ' where X is an Integer variable in miliseconds
Otherwise, I would recommend you to get rid of the SetPixel and its encosing For loops, and just fill a Rectangle using your gr Graphics :
' ...
Dim gr As System.Drawing.Graphics = Graphics.FromImage(newBitmap)
gr.Clear(Color.White)
gr.FillRectangle( _
New SolidBrush(borderColor), _
0, newBitmap.Height - borderWidthInPixels, _
newBitmap.Width, borderWidthInPixels)
gr.DrawImage(bm, 0, 0, bm.Width, bm.Height)
' ...
Remark 2 :
' you've set newBitmap to have
' bm.Heigth expanded by two times the value of borderWidthInPixels
Dim newBitmap As New Bitmap(bm.Width, bm.Height + (borderWidthInPixels * 2))
' ...
' Then you plot bm at the very top of newBitmap
gr.DrawImage(bm, 0, 0, bm.Width, bm.Height)
' that means you have a blank space at the bottom
' that height twice the value of borderWidthInPixels
' ...
' But you plot bates and designation
' at a fixed vOffset from the bottom of newBitmap
gr.DrawString(bates, , , , newBitmap.Height - voffset)
gr.DrawString(designation, , , , newBitmap.Height - voffset)
The relation between borderWidthInPixels and voffset remains unclear to me, that may mislead the formulation of clipRoom.Height above.
However, I think I've given enough material to get you where you want, and even if the clipRoom.Height formulation above is wrong, it would be very easy to fix it.
You also have to understand that I used saeed approach by default, but by reading the post I linked, you'll find other approaches, iteratives ones, which I don't like much because they are iterations CPU heavy without always beeing more precise. You can even find hints about TextToBitmap; have a try if more suitable for you, but it's more complex too.. For what you're doing, I think one or two pixels displaced outputs is not a problem.
For the vertical placement, you said ".. but the font sometime appears way too small or too large.." That's not precise enough to get a full picture of the issue. You should have posted two images samples of the "too small" case and the "too large" case. Since you didn't change Font Size in your code, I assumed the too small problem was a too large space between designation and bates, and the too large is overlapping text. There is not a single reference to a possible vertical laying out issue, so my answer doesn't elaborate on that side. That's why I don't introduce multiline logic here, which would stand for another question (if none available on SO)
VB.NET
My data is coming from an exported DXF file from various cad packages. When a circular arc (defined as a part of a true circle) is exported it is sometimes exported as a bunch of line segments rather than a circular arc.
I have a list of points and I am trying to guess if they were derived from the same circle. Basically I iterate through all the points and use a method to find the center point of a circle from three points. My intention was to compare all the calculated center points generated and make a determination if they are close to each other.
My first thought was that I could check to see if the center points are all equal but they have slight differences between them because of rounding and the underlying estimating routine that generates the point in the first place (I have no control over that).
MY second was to check the standard deviation of the x and y values of the circumference points and compare that against the standard deviation of the x,y of the centers and make some judgement from that. VB.net does not seem to have a native stdev function and I am sometimes a bit lazy.
Does anybody have a simple idea on how to determine if a list of points are all from the same circle?
Here are my functions:
To determine the center of a circle given three points:
Public Function getCenter(p1 As Point2D, p2 As Point2D, p3 As Point2D) As Point2D
Dim yDelta_a As Double = p2.Y - p1.Y
Dim xDelta_a As Double = p2.X - p1.X
Dim yDelta_b As Double = p3.Y - p2.Y
Dim xDelta_b = p3.X - p2.X
Dim center As New Point2D
Dim aSlope As Double = yDelta_a / xDelta_a
Dim bSlope As Double = yDelta_b / xDelta_b
center.X = (aSlope * bSlope * (p1.Y - p3.Y) + bSlope * (p1.X + p2.X) - aSlope * (p2.X + p3.X)) / (2 * (bSlope - aSlope))
center.Y = -1 * (center.X - (p1.X + p2.X) / 2) / aSlope + (p1.Y + p2.Y) / 2
Return center
End Function
And then to iterate the list of points and get a collection of centers. FYI...This function received a list of lines that have endpoints that are points so I do a bit of iterating to get all the correct points.
Public Function MakesCircle(lines As List(Of Line))
Dim points As New List(Of Point2D)
If lines.Count < 2 Then
Return False
Else
//Get points from lines
For i As Int16 = 0 To lines.Count - 2
points.Add(lines(i).StartPoint)
Next
points.Add(lines.Last.StartPoint)
End If
//"Prime the pump" for the center calculation loop
Dim centers As New List(Of Point2D)
Dim a As Point2D = points(0)
Dim b As Point2D = points(1)
Dim c As Point2D = points(2)
//Calc all the centers
For i As Int16 = 3 To lines.Count - 1
centers.Add(getCenter(a, b, c))
a = b
b = c
c = points(i)
Next
//This is where I need logic to determine if the points all actually belong to the same circle
Return True
End Function
You can use a GraphicsPath object to find this out. -not tested-
I figured you would be able to construct a Rectangle Structure based on the data coming in (x,y,w,h) then this make-shift algorithm would do for you.
Private Function areAllPointsInEllipsis(ellipsis As Rectangle, pts() As Point) As Boolean
Dim result As Boolean
Using gp As New System.Drawing.Drawing2D.GraphicsPath
gp.AddEllipsis(ellispsis)
result = pts.All(Function(pt) gp.IsVisible(pt))
End Using
Return result
End Function
What I did was generate all the distances from the average center point to each of the points that make up the circumference. Find the max and min distances. They you can use a threshold of a percentage of the radius - a kind of eccentricity measurement. If the difference between the max and min falls below 1% of the radius then I call it a circle.