Related
I have a piece of code that generates a dungeon in Java and Python but seems to create an infinite loop in visual basic. There might be a slight problem that I'm overlooking.
Dim Touching As Boolean = True
While Touching = True
Touching = False
'Run through all the rooms and check if they overlap
For i = 0 To numRooms
Dim aPos As Point = RoomXY(i)
Dim aDime As Point = RoomWH(i)
For j = 0 To numRooms
Dim bPos As Point = RoomXY(j)
Dim bDime As Point = RoomWH(j)
If (aPos = bPos) And (aDime = bDime) Then
Continue For
Else
'Check for overlapping
Dim H_Overlaps As Boolean = (aPos.X <= bPos.X + bDime.X) And (bPos.X <= aPos.X + aDime.X)
Dim V_Overlaps As Boolean = (aPos.Y <= bPos.Y + bDime.Y) And (bPos.Y <= aPos.Y + aDime.Y)
If H_Overlaps AndAlso V_Overlaps Then
Touching = True
'Find the minimum amount of movment that stops the squares from touching
Dim dx = Math.Min(Math.Abs((aPos.X + aDime.X) - (bPos.X + 2)), Math.Abs(aPos.X - (bPos.X + bDime.X + 2)))
Dim dy = Math.Min(Math.Abs((aPos.Y + aDime.Y) - (bPos.Y + 2)), Math.Abs(aPos.Y - (bPos.Y + bDime.Y + 2)))
If dx <= dy Then
dy = 0
Else
dx = 0
End If
If aPos.X >= bPos.X Then
RoomXY(i) = New Point(RoomXY(i).X + (dx / 2), RoomXY(i).Y)
RoomXY(j) = New Point(RoomXY(j).X - (dx / 2), RoomXY(j).Y)
Else
RoomXY(i) = New Point(RoomXY(i).X - (dx / 2), RoomXY(i).Y)
RoomXY(j) = New Point(RoomXY(j).X + (dx / 2), RoomXY(j).Y)
End If
If aPos.Y >= bPos.Y Then
RoomXY(i) = New Point(RoomXY(i).X, RoomXY(i).Y + (dy / 2))
RoomXY(j) = New Point(RoomXY(j).X, RoomXY(j).Y - (dy / 2))
Else
RoomXY(i) = New Point(RoomXY(i).X, RoomXY(i).Y - (dy / 2))
RoomXY(j) = New Point(RoomXY(j).X, RoomXY(j).Y + (dy / 2))
End If
End If
End If
Next
Next
End While
This piece is of code is given room dimensions, checks if they overlap and then shifts them away from each other. I've tried everything, but I can't seem to break the infinite loop.
I'm making a custom color-picker for a project, it's in photoshop style, i got all the other conversions to work as expected but i can't get RGBToLAB and LABToRGB to work correctly.
The problem is not just that the colors are not represented correctly but that the conversion isn't perfect too.
Sample :
LAB _ 58:0:0
XYZ _ 0.25960986510312:0.25960986510312:0.25960986510312
RGB _ {R:10 G:8 B:7 A:255}
XYZ _ 0.250358161840588:5.51162077338675:66.3836625496266
LAB _ 85.3739502460609:0:0
The initial LAB is not the same as the last LAB, this shows that the conversion is flawed. Not only am i getting the wrong colors but there's a change in values, especially when LAB.L is suppose to be constant(in this example, because that's what the slider currently is controlling)
The LAB->RGB->LAB conversion above is flawed but so is the XYZ->RGB->XYZ conversion too.
Obviously i'm not interested in converting LABToLAB but the above does point out a flaw in the conversion.
Things i've tried :
This formula on wikipedia
EasyRGB's code
This javascript code on github
This cginc code intended for unity, which is where i'm at now
Private Function LABToXYZ(LAB As LAB) As XYZ
Dim X, Y, Z As New Double
Y = ((LAB.L + 16.0) / 116.0)
X = ((LAB.A / 500.0) + Y)
Z = (Y - (LAB.B / 200.0))
Dim Less = 0.206897
If (X > Less) Then
X = Math.Pow(X, 3)
Else
X = ((X - 16.0 / 116.0) / 7.787)
End If
If (Y > Less) Then
Y = Math.Pow(Y, 3)
Else
Y = ((Y - 16.0 / 116.0) / 7.787)
End If
If (Z > Less) Then
Z = Math.Pow(Z, 3)
Else
Z = ((Z - 16.0 / 116.0) / 7.787)
End If
Return New XYZ(X, Y, Z)
End Function
Private Function XYZToRGB(XYZ As XYZ) As Color
Dim R, G, B As New Double
Dim X, Y, Z As New Double
X = (XYZ.X / 100)
Y = (XYZ.Y / 100)
Z = (XYZ.Z / 100)
R = ((X * 3.2406) + (Y * -1.5372) + (Z * -0.4986))
G = ((X * -0.9689) + (Y * 1.8758) + (Z * 0.0415))
B = ((X * 0.0557) + (Y * -0.204) + (Z * 1.057))
Dim Less As Double = 0.0031308
If (R > Less) Then
X = ((1.055 * Math.Pow(R, (1.0 / 2.4))) - 0.055)
Else
X = (R * 12.92)
End If
If (G > Less) Then
Y = ((1.055 * Math.Pow(G, (1.0 / 2.4))) - 0.055)
Else
Y = (G * 12.92)
End If
If (B > Less) Then
Z = ((1.055 * Math.Pow(B, (1.0 / 2.4))) - 0.055)
Else
Z = (B * 12.92)
End If
Return New Color(CSng(X), CSng(Y), CSng(Z))
End Function
Private Function RGBToXYZ(Color As Color) As XYZ
Dim RGB = ColorToRGB(Color)
Dim X, Y, Z As New Double
Dim Less As Double = 0.04045
If (RGB.R > Less) Then
X = Math.Pow(((RGB.R + 0.055) / 1.055), 2.4)
Else
X = (RGB.R / 12.92)
End If
If (RGB.G > Less) Then
Y = Math.Pow(((RGB.G + 0.055) / 1.055), 2.4)
Else
Y = (RGB.G / 12.92)
End If
If (RGB.B > Less) Then
Z = Math.Pow(((RGB.B + 0.055) / 1.055), 2.4)
Else
Z = (RGB.B / 12.92)
End If
X = (((X * 0.4124) + (Y * 0.3576) + (Z * 0.1805)) * 100.0)
Y = (((X * 0.2126) + (Y * 0.7152) + (Z * 0.0722)) * 100.0)
Z = (((X * 0.0193) + (Y * 0.1192) + (Z * 0.9505)) * 100.0)
Return New XYZ(X, Y, Z)
End Function
Private Function XYZToLAB(XYZ As XYZ) As LAB
Dim X, Y, Z As New Double
Dim L, A, B As New Double
Dim Less As Double = 0.008856
X = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
Y = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
Z = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
If (X > Less) Then
X = Math.Pow(X, (1.0 / 3.0))
Else
X = ((7.787 * X) + (16.0 / 116.0))
End If
If (Y > Less) Then
Y = Math.Pow(Y, (1.0 / 3.0))
Else
Y = ((7.787 * Y) + (16.0 / 116.0))
End If
If (Z > Less) Then
Z = Math.Pow(Z, (1.0 / 3.0))
Else
Z = ((7.787 * Z) + (16.0 / 116.0))
End If
L = ((116.0 * Y) - 16.0)
A = (500.0 * (X - Y))
B = (200.0 * (Y - Z))
Return New LAB(L, A, B)
End Function
Function ColorToRGB(Color As Color) As RGB
Return New RGB((Convert.ToInt32(Color.R) / 255), (Convert.ToInt32(Color.G) / 255), (Convert.ToInt32(Color.B) / 255))
End Function
Public Class RGB
Public ReadOnly Min As Double = 0
Public ReadOnly Max As Double = 1
Public Sub New()
End Sub
Public Sub New(R As Double, G As Double, B As Double)
Me.R = R
Me.G = G
Me.B = B
End Sub
Public Sub New(Color As Color)
Me.R = (Convert.ToInt32(Color.R) / 255)
Me.G = (Convert.ToInt32(Color.G) / 255)
Me.B = (Convert.ToInt32(Color.B) / 255)
End Sub
Private _R As New Double
Private _G As New Double
Private _B As New Double
Public Property R As Double
Get
Return _R
End Get
Set
_R = LimitInRange(Value, Min, Max)
End Set
End Property
Public Property G As Double
Get
Return _G
End Get
Set
_G = LimitInRange(Value, Min, Max)
End Set
End Property
Public Property B As Double
Get
Return _B
End Get
Set
_B = LimitInRange(Value, Min, Max)
End Set
End Property
Overrides Function ToString() As String
Return (_R.ToString & ":"c & _G.ToString & ":"c & _B.ToString)
End Function
End Class
Public Class XYZ
Public ReadOnly Min As Double = 0
Public ReadOnly Max As Double = 100
Public Sub New()
End Sub
Public Sub New(X As Double, Y As Double, Z As Double)
Me.X = X
Me.Y = Y
Me.Z = Z
End Sub
Private _X As New Double
Private _Y As New Double
Private _Z As New Double
Public Property X As Double
Get
Return _X
End Get
Set
_X = LimitInRange(Value, Min, Max)
End Set
End Property
Public Property Y As Double
Get
Return _Y
End Get
Set
_Y = LimitInRange(Value, Min, Max)
End Set
End Property
Public Property Z As Double
Get
Return _Z
End Get
Set
_Z = LimitInRange(Value, Min, Max)
End Set
End Property
Overrides Function ToString() As String
Return (_X.ToString & ":"c & _Y.ToString & ":"c & _Z.ToString)
End Function
End Class
Public Class LAB
Public ReadOnly Min As Double = -128
Public ReadOnly Max As Double = 127
Sub New()
End Sub
Sub New(L As Double, A As Double, B As Double)
Me.L = L
Me.A = A
Me.B = B
End Sub
Private _L As New Double
Private _A As New Double
Private _B As New Double
Property L As Double
Get
Return _L
End Get
Set
_L = LimitInRange(Value, 0, 100)
End Set
End Property
Property A As Double
Get
Return _A
End Get
Set
_A = LimitInRange(Value, Min, Max)
End Set
End Property
Property B As Double
Get
Return _B
End Get
Set
_B = LimitInRange(Value, Min, Max)
End Set
End Property
Overrides Function ToString() As String
Return (_L.ToString & ":"c & _A.ToString & ":"c & _B.ToString)
End Function
End Class
Function LimitInRange(Value As Double, Min As Double, Max As Double) As Double
Select Case Value
Case <= Min
Return Min
Case >= Max
Return Max
Case Else
Return Value
End Select
End Function
I need the code in VB.Net, that's why i'm working on converting and adapting the unity code for my project, however i am stuck and need some help.
If anybody knows what i'm doing wrong, i'll be glad to listen.
UPDATE 1:
I've tried to correct the conversion more by mismatching the two conversion methods, i'm getting closer to a perfect conversion, however i'm afraid that i might have gotten tunnel vision from working on this issue for so long.
Sample :
LAB _ 0:0:0
XYZ _ 0.262413383082537:0.262413383082537:0.262413383082537
RGB _ {R:10 G:8 B:7 A:255}
XYZ _ 0.250358161840588:0.253536089358344:0.236754082437929
LAB _ 2.29017121228677:-0.12373260790384:0.261362975778545
As you see the problem is less than before but it's still there.
Private Function LABToXYZ(LAB As LAB) As XYZ
Dim X, Y, Z As New Double
Y = ((LAB.L + 16.0) / 116.0)
X = ((LAB.A / 500.0) + Y)
Z = (Y - (LAB.B / 200.0))
Dim Less = 0.008856
If (X > Less) Then
X = Math.Pow(X, 3)
Else
X = ((X - 16.0 / 116.0) / 7.787)
End If
If (Y > Less) Then
Y = Math.Pow(Y, 3)
Else
Y = ((Y - 16.0 / 116.0) / 7.787)
End If
If (Z > Less) Then
Z = Math.Pow(Z, 3)
Else
Z = ((Z - 16.0 / 116.0) / 7.787)
End If
Return New XYZ(X * 100, Y * 100, Z * 100)
End Function
Private Function XYZToRGB(XYZ As XYZ) As Color
Dim R, G, B As New Double
Dim X, Y, Z As New Double
X = (XYZ.X / 100)
Y = (XYZ.Y / 100)
Z = (XYZ.Z / 100)
R = ((X * 3.2406) + (Y * -1.5372) + (Z * -0.4986))
G = ((X * -0.9689) + (Y * 1.8758) + (Z * 0.0415))
B = ((X * 0.0557) + (Y * -0.204) + (Z * 1.057))
Dim Less As Double = 0.0031308
If (R > Less) Then
R = ((1.055 * Math.Pow(R, (1.0 / 2.4))) - 0.055)
Else
R = (R * 12.92)
End If
If (G > Less) Then
G = ((1.055 * Math.Pow(G, (1.0 / 2.4))) - 0.055)
Else
G = (G * 12.92)
End If
If (B > Less) Then
B = ((1.055 * Math.Pow(B, (1.0 / 2.4))) - 0.055)
Else
B = (B * 12.92)
End If
Return New Color(CSng(R), CSng(G), CSng(B))
End Function
Private Function RGBToXYZ(Color As Color) As XYZ
Dim RGB = ColorToRGB(Color)
Dim X, Y, Z As New Double
Dim R, G, B As New Double
Dim Less As Double = 0.04045
If (RGB.R > Less) Then
r = Math.Pow(((RGB.R + 0.055) / 1.055), 2.4)
Else
R = (RGB.R / 12.92)
End If
If (RGB.G > Less) Then
G = Math.Pow(((RGB.G + 0.055) / 1.055), 2.4)
Else
G = (RGB.G / 12.92)
End If
If (RGB.B > Less) Then
B = Math.Pow(((RGB.B + 0.055) / 1.055), 2.4)
Else
B = (RGB.B / 12.92)
End If
R *= 100
G *= 100
B *= 100
X = ((R * 0.4124) + (G * 0.3576) + (B * 0.1805))
Y = ((R * 0.2126) + (G * 0.7152) + (B * 0.0722))
Z = ((R * 0.0193) + (G * 0.1192) + (B * 0.9505))
Return New XYZ(X, Y, Z)
End Function
Private Function XYZToLAB(XYZ As XYZ) As LAB
Dim X, Y, Z As New Double
Dim L, A, B As New Double
Dim Less As Double = 0.008856
X = XYZ.X / 100
Y = XYZ.Y / 100
Z = XYZ.Z / 100
If (X > Less) Then
X = Math.Pow(X, (1.0 / 3.0))
Else
X = ((7.787 * X) + (16.0 / 116.0))
End If
If (Y > Less) Then
Y = Math.Pow(Y, (1.0 / 3.0))
Else
Y = ((7.787 * Y) + (16.0 / 116.0))
End If
If (Z > Less) Then
Z = Math.Pow(Z, (1.0 / 3.0))
Else
Z = ((7.787 * Z) + (16.0 / 116.0))
End If
L = ((116.0 * Y) - 16.0)
A = (500.0 * (X - Y))
B = (200.0 * (Y - Z))
Return New LAB(L, A, B)
End Function
UPDATE 2:
Further testing shows an exceptionally undesired behavior in XNA.Framework.Color, resulting in any fraction being interpreted as a %.
Meaning that 200.10 would be over 200% of the max color value(255), which would cap it at the max value(255), so unless you specify integers you could end up getting a very wrong output.
I'm trying to mismatch the code from this example as well. I feel that i'm progressing, even if i had to go away from using the XNA.Framework.Color class in the conversions.
I'll update with a final solution if i find one.
UPDATE 3:
Online testing here (source code here) and here shows that my LABToXYZ is incorrect.
My results :
Lab _ 100:0:0
XYZ _ 95.047:100:100
Their results :
Lab _ 100:0:0
XYZ _ 95.05:100:108.88
Public Function LABtoXYZ(LAB As LAB) As XYZ
Dim X, Y, Z As Double
Y = ((LAB.L + 16.0) / 116.0)
X = ((LAB.A / 500.0) + Y)
Z = (Y - (LAB.B / 200.0))
Dim Pow_X = Math.Pow(X, 3.0)
Dim Pow_Y = Math.Pow(Y, 3.0)
Dim Pow_Z = Math.Pow(Z, 3.0)
Dim Less = 216 / 24389
If (Pow_X > Less) Then
X = Pow_X
Else
X = ((X - (16.0 / 116.0)) / 7.787)
End If
If (Pow_Y > Less) Then
Y = Pow_Y
Else
Y = ((Y - (16.0 / 116.0)) / 7.787)
End If
If (Pow_Z > Less) Then
Z = Pow_Z
Else
Z = ((Z - (16.0 / 116.0)) / 7.787)
End If
Return New XYZ((X * 95.047), (Y * 100.0), (Z * 108.883))
End Function
But doing LAB with all 0s result in a XYZ with all 0s, which is correct behavior, i can't tell what's wrong, it's Z that's incorrect but where is the error in my code?
Further examples here seems to suggest that my code is correct but i'm still getting an incorrect Z.
UPDATE 4:
Further refinement and re-redoing all the code, i've found that a conversion and an adaption of the examples found here, gave me the results i wanted, even tho there were some errors in that examples, notable the ^2.2 when it should have been ^2.4.
I also found some problems with precision that had to turn doubles into integers for the conversion to be perfect, but this might be the final update, unless i experience any issues, i'll leave this question open for awhile as i continue to test the code in practice.
I will come back and mark it as answered when i'm confident that the code isn't flawed.
Sample :
Test 1
LAB _ 1:0:0
XYZ _ 0.105222895807779:0.110706172533356:0.120540201839494
RGB _ 4:4:4:255
XYZ _ 0.115400959145268:0.121410793419535:0.132216354033874
LAB _ 1:0:0
Test 2
LAB _ 10:0:0
XYZ _ 1.07024816003116:1.12601992701628:1.22604427713313
RGB _ 27:27:27:255
XYZ _ 1.04175693531671:1.09600940064882:1.19355423730657
LAB _ 10:0:0
Test 3
LAB _ 100:0:0
XYZ _ 95.047:100:108.883
RGB _ 255:255:255:255
XYZ _ 95.05:100:108.9
LAB _ 100:0:0
Test 4
LAB _ 11:0:0
XYZ _ 1.19854884694432:1.26100649883144:1.37302170612264
RGB _ 29:29:29:255
XYZ _ 1.16783071832485:1.22864883569159:1.33799858206814
LAB _ 11:0:0
As seen above, there's a tiny variation that if not rounded, would cause an imperfect conversion.
The Classes
Public Class RGB
Public ReadOnly Min As Double = 0.0
Public ReadOnly Max As Double = 255.0
Public Sub New()
End Sub
Public Sub New(R As Integer, G As Integer, B As Integer)
Me.R = R
Me.G = G
Me.B = B
End Sub
Public Sub New(R As Integer, G As Integer, B As Integer, A As Integer)
Me.R = R
Me.G = G
Me.B = B
Me.A = A
End Sub
Public Sub New(R As Double, G As Double, B As Double, A As Double)
Me.R = Convert.ToInt32(R)
Me.G = Convert.ToInt32(G)
Me.B = Convert.ToInt32(B)
Me.A = Convert.ToInt32(A)
End Sub
Public Sub New(R As Double, G As Double, B As Double)
Me.R = Convert.ToInt32(R * 255)
Me.G = Convert.ToInt32(G * 255)
Me.B = Convert.ToInt32(B * 255)
End Sub
Public Sub New(Color As Color)
Me.R = Convert.ToInt32(Color.R)
Me.G = Convert.ToInt32(Color.G)
Me.B = Convert.ToInt32(Color.B)
Me.A = Convert.ToInt32(Color.A)
End Sub
Private _R As New Double
Private _G As New Double
Private _B As New Double
Private _A As Double = 255
Public Property R As Double
Get
Return _R
End Get
Set
_R = LimitInRange(Value, Min, Max)
End Set
End Property
Public Property G As Double
Get
Return _G
End Get
Set
_G = LimitInRange(Value, Min, Max)
End Set
End Property
Public Property B As Double
Get
Return _B
End Get
Set
_B = LimitInRange(Value, Min, Max)
End Set
End Property
Public Property A As Double
Get
Return _A
End Get
Set
_A = LimitInRange(Value, Min, Max)
End Set
End Property
Overrides Function ToString() As String
Return (_R.ToString & ":"c & _G.ToString & ":"c & _B.ToString & ":"c & _A.ToString)
End Function
Public Shared Operator =(Left As RGB, Right As RGB) As Boolean
If ((Left.R = Right.R) AndAlso (Left.G = Right.G) AndAlso (Left.B = Right.B) AndAlso (Left.A = Right.A)) Then
Return True
Else
Return False
End If
End Operator
Public Shared Operator <>(Left As RGB, Right As RGB) As Boolean
Return (Not (Left = Right))
End Operator
End Class
Public Class XYZ
Public ReadOnly Min As Double = 0
Public Sub New()
End Sub
Public Sub New(X As Double, Y As Double, Z As Double)
Me.X = X
Me.Y = Y
Me.Z = Z
End Sub
Private _X As New Double
Private _Y As New Double
Private _Z As New Double
Public Property X As Double
Get
Return _X
End Get
Set
_X = LimitInRange(Value, Min, 95.05)
End Set
End Property
Public Property Y As Double
Get
Return _Y
End Get
Set
_Y = LimitInRange(Value, Min, 100)
End Set
End Property
Public Property Z As Double
Get
Return _Z
End Get
Set
_Z = LimitInRange(Value, Min, 108.9)
End Set
End Property
Overrides Function ToString() As String
Return (_X.ToString & ":"c & _Y.ToString & ":"c & _Z.ToString)
End Function
End Class
Public Class LAB
Public ReadOnly Min As Double = -128
Public ReadOnly Max As Double = 127
Sub New()
End Sub
Sub New(L As Double, A As Double, B As Double)
Me.L = L
Me.A = A
Me.B = B
End Sub
Private _L As New Double
Private _A As New Double
Private _B As New Double
Property L As Double
Get
Return _L
End Get
Set
_L = LimitInRange(Value, 0, 100)
End Set
End Property
Property A As Double
Get
Return _A
End Get
Set
_A = LimitInRange(Value, Min, Max)
End Set
End Property
Property B As Double
Get
Return _B
End Get
Set
_B = LimitInRange(Value, Min, Max)
End Set
End Property
Overrides Function ToString() As String
Return (_L.ToString & ":"c & _A.ToString & ":"c & _B.ToString)
End Function
End Class
Converters
Public Function LABtoXYZ(LAB As LAB) As XYZ
Dim X, Y, Z As New Double
Y = ((LAB.L + 16.0) / 116.0)
X = ((LAB.A / 500.0) + Y)
Z = (Y - (LAB.B / 200.0))
Dim Pow_X = Math.Pow(X, 3.0)
Dim Pow_Y = Math.Pow(Y, 3.0)
Dim Pow_Z = Math.Pow(Z, 3.0)
Dim Less = (216 / 24389)
If (Pow_X > Less) Then
X = Pow_X
Else
X = ((X - (16.0 / 116.0)) / 7.787)
End If
If (Pow_Y > Less) Then
Y = Pow_Y
Else
Y = ((Y - (16.0 / 116.0)) / 7.787)
End If
If (Pow_Z > Less) Then
Z = Pow_Z
Else
Z = ((Z - (16.0 / 116.0)) / 7.787)
End If
Return New XYZ((X * 95.047), (Y * 100.0), (Z * 108.883))
End Function
Private Function XYZToRGB(XYZ As XYZ) As RGB
Dim X, Y, Z As New Double
Dim R, G, B As New Double
Dim Pow As Double = (1.0 / 2.4)
Dim Less As Double = 0.0031308
X = (XYZ.X / 100)
Y = (XYZ.Y / 100)
Z = (XYZ.Z / 100)
R = ((X * 3.24071) + (Y * -1.53726) + (Z * -0.498571))
G = ((X * -0.969258) + (Y * 1.87599) + (Z * 0.0415557))
B = ((X * 0.0556352) + (Y * -0.203996) + (Z * 1.05707))
If (R > Less) Then
R = ((1.055 * Math.Pow(R, Pow)) - 0.055)
Else
R *= 12.92
End If
If (G > Less) Then
G = ((1.055 * Math.Pow(G, Pow)) - 0.055)
Else
G *= 12.92
End If
If (B > Less) Then
B = ((1.055 * Math.Pow(B, Pow)) - 0.055)
Else
B *= 12.92
End If
Return New RGB(R, G, B)
End Function
Private Function RGBToXYZ(RGB As RGB) As XYZ
Dim X, Y, Z As New Double
Dim R, G, B As New Double
Dim Less As Double = 0.04045
R = (RGB.R / 255)
G = (RGB.G / 255)
B = (RGB.B / 255)
If (R > Less) Then
R = Math.Pow(((R + 0.055) / 1.055), 2.4)
Else
R = (R / 12.92)
End If
If (G > Less) Then
G = Math.Pow(((G + 0.055) / 1.055), 2.4)
Else
G = (G / 12.92)
End If
If (B > Less) Then
B = Math.Pow(((B + 0.055) / 1.055), 2.4)
Else
B = (B / 12.92)
End If
X = ((R * 0.4124) + (G * 0.3576) + (B * 0.1805))
Y = ((R * 0.2126) + (G * 0.7152) + (B * 0.0722))
Z = ((R * 0.0193) + (G * 0.1192) + (B * 0.9505))
Return New XYZ(X * 100, Y * 100, Z * 100)
End Function
Private Function XYZToLAB(XYZ As XYZ) As LAB
Dim X, Y, Z As New Double
Dim L, A, B As New Double
Dim Less As Double = 0.008856
Dim Pow As Double = (1.0 / 3.0)
X = ((XYZ.X / 100) / 0.9505)
Y = (XYZ.Y / 100)
Z = ((XYZ.Z / 100) / 1.089)
If (X > Less) Then
X = Math.Pow(X, Pow)
Else
X = ((7.787 * X) + (16.0 / 116.0))
End If
If (Y > Less) Then
Y = Math.Pow(Y, Pow)
Else
Y = ((7.787 * Y) + (16.0 / 116.0))
End If
If (Z > Less) Then
Z = Math.Pow(Z, Pow)
Else
Z = ((7.787 * Z) + (16.0 / 116.0))
End If
L = ((116.0 * Y) - 16.0)
A = (500.0 * (X - Y))
B = (200.0 * (Y - Z))
'We solve the precision problem by rounding to nearest integer
'This makes the conversion perfect.
Return New LAB(CInt(L), CInt(A), CInt(B))
End Function
Further testing is required before i'll mark this as solved.
UPDATE 5: Haven't had any issues so far... I don't know how to mark this as answered when there is only the question posted.
The full free code and more can be found here.
I have not parsed all your code, but an issue in your first code block, in the function RGBToXYZ
X = (((X * 0.4124) + (Y * 0.3576) + (Z * 0.1805)) * 100.0)
Y = (((X * 0.2126) + (Y * 0.7152) + (Z * 0.0722)) * 100.0)
Z = (((X * 0.0193) + (Y * 0.1192) + (Z * 0.9505)) * 100.0)
Return New XYZ(X, Y, Z)
You do the matrix for X, then use X again for the matrix for Y... but now X is at the new value! This is not a place to be skimpy on variables.
This should be instead something like this:
Dim Xout, Yout, Zout As New Double
Xout = ((X * 0.4124) + (Y * 0.3576) + (Z * 0.1805))
Yout = ((X * 0.2126) + (Y * 0.7152) + (Z * 0.0722))
Zout = ((X * 0.0193) + (Y * 0.1192) + (Z * 0.9505))
Return New XYZ(Xout, Yout, Zout)
Also, I suggest keeping XYZ as a 0.0-1.0 range.
And for other things:
LABToXYZ is missing a needed illuminant conversion. It needs to return:
X = (X * 0.95047)
Z = (Z * 1.08883)
And then XYZtoLAB has:
X = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
Y = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
Z = ((XYZ.X / 95.047) + (XYZ.Y / 100) + (XYZ.Z / 108.883))
Which is just making X Y and Z all the same...
Should be (assuming keeping XYZ as 0-1):
X = (XYZ.X / 0.95047)
Y = (XYZ.Y)
Z = (XYZ.Z / 1.08883)
I just realized that you solved your own question — I'll leave this here though in case someone runs across it in search of similar answers.
So I have a "main" function (SolveSixODES) that calls a secondary function (AllODEs). And when it does this, the x value in the main function gets modified. I don't understand how this can be possible, seeing as it is not a global variable.
Here is the code, my inputs I used are as follows:
x=0, xmax=3, y=0-6, h=0.1, error=0.1
Public Function SolveSixODE(x As Double, xmax As Double, Y As Range, h As Double, error As Double) 'Weird bug: You must leave the first y4 value blank
Dim i As Integer, k(7, 7) As Double, j As Integer, m As Integer 'k(Order #, equation #)
Dim Y5(7) As Double, Y4(7) As Double, Y4Old(7) As Double
Dim delta0(7) As Double, delta1(7) As Double, delRatio(7) As Double, Rmin As Double
For i = 1 To 6 'Moving the input data so it can acutally be used
Y4(i) = Y(i)
Next i
While x < xmax
If x + h < xmax Then
x = x + h
Else
h = xmax - x
x = xmax
End If
For j = 1 To 6 'j is the order i is equation number
For i = 1 To 6 'Calculating all of the k(1) values for eq 1 to 6
k(j, i) = AllODES(x, Y4, i, j, k, h) '!!!!!SOME HOW THIS LOOP MAKES X negative...!!!!!!!
Next i
Next j
For i = 1 To 6
Y4Old(i) = Y4(i) 'Saving old y4 value to calc delta0
Y4(i) = Y4(i) + h * (k(1, i) * (37 / 378) + k(3, i) * (250 / 621) + k(4, i) * (125 / 594) + k(6, i) * (512 / 1771))
Y5(i) = Y4(i) + h * (k(1, i) * (2825 / 27648) + k(3, i) * (18575 / 48384) + k(4, i) * (13525 / 55296) + k(5, i) * (277 / 14336) + k(6, i) * (0.25))
delta0(i) = error * (Abs(Y4Old(i)) + Abs(h * AllODES(x, Y4Old, i, 1, k, h))) 'First order because we don't want to use the k vals
delta1(i) = Abs(Y5(i) - Y4(i))
delRatio(i) = Abs(delta0(i) / delta1(i)) 'Ratio of errors
Next i
Rmin = delRatio(1)
For i = 2 To 6
If delRatio(i) < Rmin Then
Rmin = delRatio(i) 'Determine the smallest error ratio
End If
Next i
If Rmin < 1 Then 'If this is true then the step size was too big must repeat step
x = x - h 'Set x and y's back to previous values
For i = 1 To 6
Y4(i) = Y4Old(i)
Next i
h = 0.9 * h * Rmin ^ 0.25 'adjust h value; 0.9 is a safety factor
Else
h = 0.9 * h * Rmin ^ 0.2 'Otherwise, we march on
End If
m = m + 1
Wend
SolveSixODE = Y4
End Function
Public Function AllODES(x As Double, Y() As Double, EqNumber As Integer, order As Integer, k() As Double, h As Double) As Double
Dim conc(7) As Double, i As Integer, j As Integer
If order = 1 Then
x = x - h
For i = 1 To 6 'Movin the data so I can use it
conc(i) = Y(i) 'also adjusting the x and y values for RK4 (Cash Karp values)
Next i
ElseIf order = 2 Then
x = x - h + h * 0.2
For i = 1 To 6
conc(i) = Y(i) + h * k(1, i) * 0.2
Next i
ElseIf order = 3 Then
x = x - h + 0.3 * h
For i = 1 To 6
conc(i) = Y(i) + h * (0.075 * k(1, i) + 0.225 * k(2, i))
Next i
ElseIf order = 4 Then
x = x - h + 0.6 * h
For i = 1 To 6
conc(i) = Y(i) + h * (0.3 * k(1, i) - 0.9 * k(2, i) + 1.2 * k(3, i))
Next i
ElseIf order = 5 Then
x = x - h + h
For i = 1 To 6
conc(i) = Y(i) + h * ((-11 / 54) * k(1, i) + 2.5 * k(2, i) - (70 / 27) * k(3, i) + (35 / 27) * k(4, i))
Next i
ElseIf order = 6 Then
x = x - h + 0.875 * h
For i = 1 To 6
conc(i) = Y(i) + h * ((1631 / 55296) * k(1, i) + (175 / 512) * k(2, i) + (575 / 13824) * k(3, i) + (44275 / (110592) * k(4, i) + (253 / 4096) * k(5, i)))
Next i
Else
MsgBox ("error")
End If
If EqNumber = 1 Then 'These are the actual equations
AllODES = x + Y(1)
ElseIf EqNumber = 2 Then
AllODES = x
ElseIf EqNumber = 3 Then
AllODES = Y(3)
ElseIf EqNumber = 4 Then
AllODES = 2 * x
ElseIf EqNumber = 5 Then
AllODES = 2 * Y(2)
ElseIf EqNumber = 6 Then
AllODES = 3 * x
Else
MsgBox ("You entered an Eq Number that was dumb")
End If
End Function
It's possible that it is something really trivial that I missed but this seems to contradict my knowledge of how variables work. So if you understand how the function is able to manipulate a variable from another function in this case, I would appreciate any advice and/or explanation!
Thanks in advance!
the x value in the main function gets modified. I don't understand how this can be possible, seeing as it is not a global variable
This is normal because you are passing x by reference to the function AllODES and you do change it there. When the keyword ByVal is not explicitly specified in the function/sub prototype, the default passing mechanism is ByRef, that is, by reference.
Public Function AllODES(x As Double, ...
means
Public Function AllODES(ByRef x As Double, ....
We observe that x is manipulated in this function, so the change will appear in the caller. If you want that the change of x does not report back in the caller's scope, pass x by value:
Public Function AllODES(ByVal x As Double, ....
' ^^^^^
Only in this case the x of the caller and the x of the callee will be two different variables.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
Form 2 is to enter ladder base and its off set value and for snakes where the snake head is and skakes off set value.
Not able to figure out why its not working . When the values are entered to show simulation it show's up error sandl is private and the other one is the validation one .
Public Class Form2
Dim sandl(99) As Integer
Dim snakeshead As TextBox()
Dim snakesoffset As TextBox()
Dim ladderfoot As TextBox()
Dim ladderoffset As TextBox()
Dim rnd As Random = New Random
Sub initialise()
For i = 0 To 99
sandl(i) = 0 ' reset data
Next
End Sub
Sub snake()
snakeshead = {txthead1, txthead2, txthead3, txthead4, txthead5, txthead6, txthead7, txthead8, txthead9, txthead10}
snakesoffset = {txtoffset1, txtoffset2, txtoffset3, txtoffset4, txtoffset5, txtoffset6, txtoffset7, txtoffset8, txtoffset9, txtoffset10}
' SnakeHead(i).Text = (i + 81).ToString
' SnakeOffset(i).Text = "10" '(i + 10).ToString
For i As Integer = 0 To 9
While True
Dim base = rnd.Next(90) + 11
If sandl(base - 1) <> 0 Then
Continue While
End If
Dim offset = rnd.Next(20) + 10
If base - offset < 1 Then
Continue While
End If
snakeshead(i).Text = base.ToString
snakesoffset(i).Text = offset.ToString
sandl(base - 1) = -offset
Exit While
End While
Next
End Sub
Sub ladders()
ladderfoot = {txtladder1, txtladder2, txtladder3, txtladder4, txtladder5, txtladder6, txtladder7, txtladder8, txtladder9, txtladder10}
ladderoffset = {txtladderoffset1, txtladderoffset2, txtladderoffset3, txtladderoffset4, txtladderoffset5, txtladderoffset6, txtladderoffset7, txtladderoffset8, txtladderoffset9, txtladderoffset10}
'For i As Integer = 0 To 9
' LadderFoot(i).Text = (i + 11).ToString
' LadderOffset(i).Text = "10"
For i As Integer = 0 To 99
sandl(i) = 0 'reset data
Next
For i As Integer = 0 To 9
While True
Dim base = rnd.Next(90) + 1
If sandl(base - 1) <> 0 Then
Continue While
End If
Dim offset = rnd.Next(20) + 10
If base + offset > 100 Then
Continue While
End If
ladderfoot(i).Text = base.ToString
ladderoffset(i).Text = offset.ToString
sandl(base - 1) = offset
Exit While
End While
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
For i As Integer = 0 To 99
sandl(i) = 0 'reset data
Next
Dim valid = Validate(ladderfoot, ladderoffset, +1, "Ladder")
If (valid) Then
valid = Validate(snakeshead, snakesoffset, -1, "Snake")
End If
If (valid) Then
'Form3 = New Form3
Form3.ShowDialog()
End If
End Sub
Private Function Validate(tbBase() As TextBox, tbOffset() As TextBox, delta As Integer, s As String) As Boolean
For i As Integer = 0 To 9
Dim base As Integer
If ((Not Integer.TryParse(tbBase(i).Text.Trim(), base)) OrElse (base < 1) OrElse (base > 100) OrElse (sandl(base - 1) <> 0)) Then
MessageBox.Show(s & (i + 1).ToString() & " base is invalid.")
tbBase(i).Select()
tbBase(i).SelectAll()
Return False
End If
base -= 1 'zero based
Dim offset As Integer
If ((Not Integer.TryParse(tbOffset(i).Text.Trim(), offset)) OrElse (offset < 10) OrElse (offset > 30) OrElse (base + offset * delta < 0) OrElse (base + offset * delta >= 100)) Then
MessageBox.Show(s & (i + 1).ToString() & " offset is invalid.")
tbOffset(i).Select()
tbOffset(i).SelectAll()
Return False
End If
sandl(base) = offset * delta 'write offset
Next
Return True
End Function
End Class
Public Class Form3
Enum EState
Dice
Move
Slide
Wait
Win
End Enum
Dim Fnt = New Font("Arial", 16)
Dim FntBig = New Font("Arial", 256)
Dim Frame As Integer = -1 'counter
Dim State = EState.Dice
Dim Rnd As Random = New Random
Dim Dice As Integer
Dim Pos As Point = New Point(32, 640 + 32)
Dim CurrentIndex As Integer = -1
Dim NextIndex As Integer
Dim TargetIndex As Integer
Private Sub Form3_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dice = 0
Frame = -1
State = EState.Dice
Pos = New Point(32, 640 + 32)
CurrentIndex = -1
End Sub
Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint
DrawBackground(e.Graphics)
Frame += 1
Dim oldState = State
Select Case State
Case EState.Dice
If Frame = 0 Then
Dice = Rnd.Next(6) + 1 'roll dice
TargetIndex = CurrentIndex + Dice
NextIndex = CurrentIndex
ElseIf Frame >= 63 Then
If CurrentIndex + Dice < 100 Then
State = EState.Move 'valid dice
Else
State = EState.Wait 'invalid dice
End If
Dice = 0
End If
Case EState.Move
If Frame Mod 64 = 0 Then
CurrentIndex = NextIndex
If CurrentIndex = TargetIndex Then
If CurrentIndex < 99 Then 'not win
If Form2.sandl(CurrentIndex) <> 0 Then
State = EState.Slide 'snake or ladder
Else
State = EState.Dice 'empty tile
End If
TargetIndex = CurrentIndex + Form2.sandl(CurrentIndex)
Else
State = EState.Win 'win
End If
Else
NextIndex = CurrentIndex + 1 'move
End If
Else
Dim c = GetCoordinate(CurrentIndex)
Dim n = GetCoordinate(NextIndex)
Dim dx = (n.X - c.X)
Dim dy = (n.Y - c.Y)
Pos.X = c.X * 64 + (dx * (Frame Mod 64)) + 32
Pos.Y = c.Y * 64 + (dy * (Frame Mod 64)) + 32
End If
Case EState.Slide
If Frame >= 63 Then
CurrentIndex = TargetIndex
If CurrentIndex < 99 Then
State = EState.Dice 'not win
Else
State = EState.Win 'win
End If
Else
Dim c = GetCoordinate(CurrentIndex)
Dim n = GetCoordinate(TargetIndex)
Dim dx = (n.X - c.X)
Dim dy = (n.Y - c.Y)
Pos.X = c.X * 64 + (dx * (Frame Mod 64)) + 32
Pos.Y = c.Y * 64 + (dy * (Frame Mod 64)) + 32
End If
Case EState.Wait
If Frame >= 63 Then
State = EState.Dice
End If
End Select
e.Graphics.FillEllipse(Brushes.Blue, Pos.X - 16, Pos.Y - 16, 32, 32) 'draw player
If Dice > 0 Then
Dim size = e.Graphics.MeasureString(Dice.ToString, FntBig)
e.Graphics.DrawString(Dice.ToString, FntBig, Brushes.Black, 320 - size.Width / 2, 320 - size.Height / 2) 'print dice
End If
If State <> oldState Then
Frame = -1 'reset counter
End If
If State <> EState.Win Then
PictureBox1.Invalidate() 'schedule next paint
End If
End Sub
Private Sub DrawBackground(g As Graphics)
For y As Integer = 0 To 9
For x As Integer = 0 To 9
If (((x + y) Mod 2) = 0) Then
g.FillRectangle(Brushes.LightGray, x * 64, y * 64, 64, 64) 'dark rectangle
End If
Dim z = (9 - y) * 10 + x + 1
If y Mod 2 = 0 Then
z = (9 - y) * 10 + (9 - x) + 1
End If
g.DrawString(z.ToString, Fnt, Brushes.Black, x * 64, y * 64) 'number
Next
Next
For i As Integer = 0 To 99
If Form2.sandl(i) <> 0 Then
Dim base = GetCoordinate(i)
Dim offset = GetCoordinate(i + Form2.sandl(i))
If Form2.sandl(i) > 0 Then 'ladder
Dim delta = Math.Abs(base.X - offset.X) + 4
g.DrawLine(Pens.Green, base.X * 64 + 32 - delta, base.Y * 64 + 32, offset.X * 64 + 32 - delta, offset.Y * 64 + 32) 'left part
g.DrawLine(Pens.Green, base.X * 64 + 32 + delta, base.Y * 64 + 32, offset.X * 64 + 32 + delta, offset.Y * 64 + 32) 'right part
Else 'snake
g.DrawLine(Pens.Red, base.X * 64 + 32, base.Y * 64 + 32, offset.X * 64 + 32, offset.Y * 64 + 32) 'red line
End If
End If
Next
End Sub
Private Function GetCoordinate(i As Integer) As Point
Dim result As Point
result.Y = 9 - (i \ 10)
result.X = i Mod 10
If result.Y Mod 2 = 0 Then
result.X = 9 - result.X
End If
Return result
End Function
End Class
In Form2, change your declaration from
Dim sandl(99) As Integer
to
Public sandl(99) As Integer
This would allow Form3 to access your integer array
Rename your Validate method to something else, like ValidateTextBoxes, or if you intend to overload the base.Validate, then declare as
Private Overloads Function Validate
Not sure how to solve this. "a" was meant to start from 1 to NumData, but I have deliberately change the start from 44200 to check the ELSEIF. For NumData = 117,350,
I would expect the 3rd ELSEIF to be activated. Instead throughout the whole run, it only step-into the first ELSEIF even though the "a" value does not meet the conditions.
What should I do?
For a = 44200 To NumData 'Int1
If a > 1 Then
If UCase(Trim(Range1(a, 3))) = UCase(Trim(Range1(a - 1, 3))) Then
GoTo Line1 'Next count loop if next Platform name the same
End If
End If
For b = 1 To NumData
lat1 = Range1(a, 5)
lat2 = Range1(b, 5)
long1 = Range1(a, 6)
long2 = Range1(b, 6)
CompRad = Dist(lat1, lat2, long1, long2)
If (CompRad <= Radius And CompRad >= 0) Then
z = CLng(NumData / 8)
If a <= CLng(NumData / 8) Then
For c = 1 To 6
Range2(d, c) = Range1(b, c)
Next c
Acc_Sum2 = Acc_Sum2 + Range2(d, 4)
d = d + 1
ElseIf CLng(NumData / 8) < a <= 2 * CLng(NumData / 8) Then
z = 2 * CLng(NumData / 8)
For c = 1 To 6
Range3(e, c) = Range1(b, c)
Next c
Acc_Sum3 = Acc_Sum3 + Range3(e, 4)
e = e + 1
ElseIf 2 * CLng(NumData / 8) < a <= 3 * CLng(NumData / 8) Then
For c = 1 To 6
Range4(f, c) = Range1(b, c)
Next c
Acc_Sum4 = Acc_Sum4 + Range4(f, 4)
f = f + 1
ElseIf 3 * CLng(NumData / 8) < a <= 4 * CLng(NumData / 8) Then
z = 3 * CLng(NumData / 8)
For c = 1 To 6
Range5(g, c) = Range1(b, c)
Next c
Acc_Sum5 = Acc_Sum5 + Range5(g, 4)
g = g + 1
ElseIf 4 * CLng(NumData / 8) < a <= 5 * CLng(NumData / 8) Then
For c = 1 To 6
Range6(h, c) = Range1(b, c)
Next c
Acc_Sum6 = Acc_Sum6 + Range6(h, 4)
h = h + 1
ElseIf 5 * CLng(NumData / 8) < a <= 6 * CLng(NumData / 8) Then
For c = 1 To 6
Range7(i, c) = Range1(b, c)
Next c
Acc_Sum7 = Acc_Sum7 + Range7(i, 4)
i = i + 1
ElseIf 6 * CLng(NumData / 8) < a <= 7 * CLng(NumData / 8) Then
For c = 1 To 6
Range8(j, c) = Range1(b, c)
Next c
Acc_Sum8 = Acc_Sum8 + Range8(j, 4)
j = j + 1
ElseIf 7 * CLng(NumData / 8) < a <= NumData Then
For c = 1 To 6
Range9(k, c) = Range1(b, c)
Next c
Acc_Sum9 = Acc_Sum9 + Range9(k, 4)
k = k + 1
End If
End If
Next b
Line1:
Next a
Your conditions like:
1 < a <= 10
are always true. First part (1 < a) evaluates to True or False and then it is converted to integer (True = 1, False = 0). Both values are <=10.
You should change thes conditions to:
(1 < a) And (a <= 10)
Brackets are optional, comparison operators have higher precedence.
user3964075 nailed the core issue. I think making the code a little bit more readable will help to filter out other possible trouble areas. Why do you set the value of 'z' and then not use it?
z = CLng(NumData / 8)
Where are the variables d, e, f...; Range2, Range3, Range4...; Acc_Sum2, Acc_Sum3... assigned, and what are they doing? Can each group be replaced by a single variable?
You may also want to create a simple Between function
to clean up a lot of your conditional statements.
Public Function Between(x As Integer, min As Integer, max As Integer) As Boolean
Between = x <= max And x >= min
End Function
Thanks, user3964075 & Carl for your prompt response. It's worked! Carl, the z variable was slotted in when I was trying to figure out the problem. I thought it would be too much to post the whole code. It does require a lot of cleaning up. Appreciate your tips.