Ich habe deine Dll so kompiliert: nim c -d:release -o:FuzzySearch.dll FuzzySearch.nim
Du musst das so aufrufen: nim c -d:release --app:lib FuzzySearch.nim
Ich habe deine Dll so kompiliert: nim c -d:release -o:FuzzySearch.dll FuzzySearch.nim
Du musst das so aufrufen: nim c -d:release --app:lib FuzzySearch.nim
Da fühle ich mich gleich schlecht, dass ich soetwas ähnliches in der SB nachgefragt habe,
Ich gebe zu, dass Deine Frage in der SB den Ausschlag dafür gab, aber ich finde, dass man sowas in AutoIt durchaus mal gebrauchen kann.
Und es war eine Übung, um in Nim zu programmieren. ![]()
Im Übrigen ist das Ganze wirklich schnell. Bei mir dauert die Suche (so wie im Beispiel) nur rund 280ms. Bei 207336 Wörtern ist das nicht schlecht, denke ich.
Müsste man mal mit purem AutoIt vergleichen... ![]()
Wenn man in AutoIt ein Wort in einem Text sucht, nimmt man normalerweise StringInStr dafür oder bastelt etwas mit StringRegExp.
Das ist meistens ausreichend und auch schnell genug.
Wenn man allerdings eine "unscharfe" Suche möchte, wird es kompliziert und langsam. Die sogenannte Levenshtein-Distanz (oder in Prozent umgerechnet) gibt die Ähnlichkeit zweier Wörter zurück.
In AutoIt ist die Funktion aber sehr zeitaufwändig und wenn man einen größeren Text durchsuchen will, dauert es sehr lange.
Da ich die Levenshtein-Distanz bereits in Nim übersetzt habe, dachte ich mir, das kann ich dann doch auch für AutoIt zur Verfügung stellen (als DLL).
Die Suche habe ich dann auch gleich in Nim geschrieben, so wird es noch etwas schneller.
Edit 03.10.2020:
Ich habe den Nim-Quellcode jetzt dahingehend geändert, dass gleich alle Fundstellen (als String) zurückgegeben werden.
Das hat den Vorteil, dass bei sehr vielen Fundstellen nur ein DllCall von AutoIt aus erforderlich ist. Im Worstcase-Fall (Suchbegriff: ein Buchstabe und Null Prozent) würde sonst für jedes Wort ein Dll-Call nötig sein, was recht zeitaufwändig ist.
Edit 06.10.2020:
Die Nim-interne Funktion zur Berechnung der Levenshtein-Distance editDistanceAscii() ist deutlich effektiver, als die von mir konvertierte Funktion. Die Laufzeit des Beispiels schrumpft somit von vorher 238ms auf nunmehr 66ms.
Ich finde, das ist eine neue Version wert. ![]()
Der Nim-Sourcecode sieht so aus (zum compilieren des Codes als Dll den Compiler folgendermaßen aufrufen: nim c -d:release --app:lib FuzzySearch.nim
import strutils, std/editdistance
# Levenshtein-Distanz in Prozent berechnen
proc lsDistancePercent*(s, t: cstring): int {.stdcall,exportc,dynlib.} =
let n = max(s.len, t.len)
if n == 0 or s == t: return 100
return int(100 / n * float(n - editDistanceAscii($s, $t)))
# Ein Objekt zum speichern der Woerter
type
FuzzySearch* = ref object of RootObj
sSearch*: seq[string]
let oFS = FuzzySearch() # Objekt erstellen
# Funktion, zum erstellen der Woerter aus dem uebergebenen String
proc load*(sData: cstring): int {.stdcall,exportc,dynlib.} =
if sData == "": return 0
oFS.sSearch = splitWhitespace($sData, -1)
return 1
# Funktion, um die Anzahl der Woerter zurueckzugeben
proc getCount*(): int {.stdcall,exportc,dynlib.} =
return oFS.sSearch.len
# Funktion, um ein bestimmtes Wort zurueckzugeben
proc getWord*(iIndex: int): cstring {.stdcall,exportc,dynlib.} =
if iIndex < 0 or iIndex > oFS.sSearch.len - 1: return ""
return oFS.sSearch[iIndex]
# Funktion, fuer die Levenshtein-Suche
# Parameter:
# t = Suchbegriff
# s = ab dieser Wort-Position suchen
# p = Prozentwert, fuer die Suche
# Rueckgabe:
# Ein String mit den Positionen der gefundenen Worte (durch Kommata getrennt),
# oder ein Leerstring, wenn nicht gefunden, bzw. Fehler
proc search*(t: cstring, s, p: int): cstring {.stdcall,exportc,dynlib.} =
if oFS.sSearch.len == 0: return ""
if s > oFS.sSearch.len - 1 or s < 0: return ""
var
sReturn: seq[string]
iPercent: int = p
if iPercent > 99: iPercent = 99
if iPercent < 0: iPercent = 0
for iIndex in s + 1 .. oFS.sSearch.len - 1:
if (lsDistancePercent(oFS.sSearch[iIndex], $t) >= iPercent): sReturn.add($iIndex)
return join(sReturn, ",")
Alles anzeigen
Für die, die kein Nim installiert haben, habe ich die DLL bereits compiliert mit ins ZIP-Archiv (Anhang) gepackt.
Das AutoIt-Beispiel sieht dann so aus:
#include <Array.au3>
#AutoIt3Wrapper_UseX64=y
Global $hDll = DllOpen(@ScriptDir & '\FuzzySearch.dll')
Global $sWord1, $sWord2, $aRet, $iPos, $iPercent, $sFound
; Test 1 = Levenshtein-Distanz in Prozent
; =======================================
$sWord1 = 'Zuckerkuchen' ; diese beiden Woerter
$sWord2 = 'Butterkuchen' ; sollen verglichen werden
$aRet = DllCall($hDll, 'int', 'lsDistancePercent', 'str', $sWord1, 'str', $sWord2)
If @error Then Exit
ConsoleWrite('Übereinstimmung: ' & $aRet[0] & '%' & @CRLF) ; in $aRet[0] steht das Ergebnis (als Integerzahl, in Prozent)
; Test 2 = Die Fuzzy-Suche in einem grossen Textdokument
; ======================================================
$sWord1 = FileRead(@ScriptDir & '\bgb.txt') ; ein Textdokument laden (BGB, > 14.000 Zeilen, 1.5 MB)
$aRet = DllCall($hDll, 'int', 'load', 'str', $sWord1) ; und den Text an die DLL uebergeben
If @error Then Exit
ConsoleWrite('Load ok: ' & ($aRet[0] == 1) & @CRLF)
$aRet = DllCall($hDll, 'int', 'getCount') ; Anzahl der Woerter ermitteln
If @error Then Exit
ConsoleWrite('Count: ' & $aRet[0] & @CRLF)
$aRet = DllCall($hDll, 'str', 'getWord', 'int', 85271) ; ein bestimmtes Word aus dem Text holen
If @error Then Exit
ConsoleWrite('Word: ' & $aRet[0] & @CRLF)
$sWord2 = 'Richtlinie' ; das Suchwort
$iPos = 0 ; Startwert. Ab diesem *Wort* (*nicht* Buchstaben) wird gesucht.
$iPercent = 66 ; mit welcher Genauigkeit soll gesucht werden (in Prozent)
$iTimer = TimerInit()
; Die Rueckgabe besteht aus einem String mit den Positionen der gefundenen
; Woerter (durch Kommata getrennt) oder ein Leerstring, wenn nichts gefunden
; wurde bzw. ein Fehler aufgetreten ist.
$aRet = DllCall($hDll, 'str', 'search', 'str', $sWord2, 'int', $iPos, 'int', $iPercent)
If @error Then Exit ConsoleWrite('Error: ' & @error & @CRLF)
ConsoleWrite('Timer: ' & TimerDiff($iTimer) & @CRLF)
ConsoleWrite('Found on Position: ' & $aRet[0] & @CRLF)
DllClose($hDll)
Global $aFound = StringSplit($aRet[0], ',', 2)
_ArrayDisplay($aFound)
Alles anzeigen
Wichtig ist noch, dass ihr das AutoIt-Script als 64-Bit-Version ausführt #AutoIt3Wrapper_UseX64=y, weil Nim eine 64-Bit-DLL erstellt.
Alle Quellcodes und den Beispieltext (BGB mit 207336 Wörtern) findet ihr im ZIP-Archiv.
Warum compilierst Du das Script nicht als CUI-Programm?
Dann funktioniert ganz einfach ConsoleWrite:
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Outfile_x64=GetWindowspos.exe
#AutoIt3Wrapper_Change2CUI=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#include <Array.au3>
ConsoleWrite(@CRLF & "Please be patient until preparations are done...!" & @CRLF & @CRLF)
$sName = InputBox("Eingabe Fenstername", "Bitte gib den Fenstername ein:")
Local $aPos = WinGetPos($sName)
_ArrayDisplay($aPos, $sName)
Alles anzeigen
Ich habe mir mal das Wireshark-Protokoll angesehen. Die Zieladresse ist wohl eher: 192.168.178.55
Aber egal.
Hier die Aufzeichnungen von den ersten drei Paketen:
0000 b8 27 eb 23 28 83 d8 cb 8a ba 77 44 08 00 45 00 .'.#(.....wD..E.
0010 00 2b 36 a6 00 00 80 11 cb 8d c0 a8 05 06 c0 a8 .+6.............
0020 b2 37 2b 48 2b 48 00 17 24 1a 70 07 01 06 1e 14 .7+H+H..$.p.....
0030 00 00 ff 00 03 03 54 61 67 ......Tag
0000 b8 27 eb 23 28 83 d8 cb 8a ba 77 44 08 00 45 00 .'.#(.....wD..E.
0010 00 2b 36 a7 00 00 80 11 cb 8c c0 a8 05 06 c0 a8 .+6.............
0020 b2 37 2b 48 2b 48 00 17 24 06 70 07 01 06 1e 28 .7+H+H..$.p....(
0030 00 00 ff 00 03 03 54 61 67 ......Tag
0000 b8 27 eb 23 28 83 d8 cb 8a ba 77 44 08 00 45 00 .'.#(.....wD..E.
0010 00 2b 36 a8 00 00 80 11 cb 8b c0 a8 05 06 c0 a8 .+6.............
0020 b2 37 2b 48 2b 48 00 17 23 2e 70 07 01 06 1f 00 .7+H+H..#.p.....
0030 00 00 ff 00 03 03 54 61 67 ......Tag
Alles anzeigen
Jeweils die letzten 15 Bytes enthalten die übertragenen Daten (ab 70 07 01).
Was diese "70 07 01" sind kann ich nicht sagen, aber danach die drei Bytes scheinen die Uhrzeit zu sein.
Du sagtest, dass Du die Aufzeichnung um 06:30 Uhr gestartet und bis 06:40 Uhr laufen gelassen hast.
1. Paket: Hex 06 1e 14 = 06:30:20
2. Paket: Hex 06 1e 28 = 06:30:40
3. Paket: Hex 06 1f 00 = 06:31:00
und so geht es weiter bis
n. Paket: Hex 06 28 28 = 06:40:40
Ideen?
So:
import wNim, winim
var
dc = MemoryDC()
bmp = Bitmap(16, 16)
dc.selectObject(bmp)
dc.clear(Brush(wBlueBrush)) # <- Standard-Brush
let blueIcon = Icon(bmp)
dc.clear(Brush(color=0x000000FF)) # <- im BGR-Format
let redIcon = Icon(bmp)
dc.clear(Brush(wWhiteBrush)) # <- Standard-Brush
dc.drawCheckMark(0, 0, 15, 15) # Haken zeichnen (x, y, w, h)
let checkIcon = Icon(bmp)
let
app = App()
frame = Frame(title="Test ImageList",size=(600,300))
panel = frame.Panel()
list = panel.ListCtrl(size=(560,240), pos=(10,10), style=wLcReport or wBorderDouble or wLcSingleSel)
list.setExtendedStyle(LVS_EX_GRIDLINES) # <- Extended Style
var il = ImageList(size=(16,16), mask=false, initialCount=0)
il.add(blueIcon)
il.add(redIcon)
il.add(checkIcon)
list.setImageList(il)
list.appendColumn(text="Column 0", width=150)
list.setColumnFormat(0, wListFormatLeft)
list.appendColumn(text="Column 1", width=110, format=wListFormatRight)
list.appendColumn(text="Column 2", width=110, format=wListFormatRight)
list.appendColumn(text="Column 3", width=wListAutosizeUseHeader)
var imgIndex: int
for i in 0..10:
imgIndex = 1
if (i mod 2) == 0: imgIndex = 0
if (i mod 3) == 0: imgIndex = 2
list.appendItem([$i&"-0", $i&"-1", $i&"-2", $i&"-3"], imgIndex)
frame.wIdExit do ():
frame.delete()
frame.center()
frame.show()
app.mainLoop()
Alles anzeigen
Als kleine Ergänzung:
Für so einfache "Grafiken" braucht man keine externen Imagedateien. Die kann man auch direkt zeichnen:
import wNim
var
dc = MemoryDC()
bmp = Bitmap(16, 16)
dc.selectObject(bmp)
dc.clear(Brush(wBlueBrush)) # <- Standard-Brush
let blueIcon = Icon(bmp)
dc.clear(Brush(color=0x000000FF)) # <- im BGR-Format
let redIcon = Icon(bmp)
dc.clear(Brush(wWhiteBrush)) # <- Standard-Brush
dc.drawCheckMark(0, 0, 15, 15) # Haken zeichnen (x, y, w, h)
let checkIcon = Icon(bmp)
let
app = App()
frame = Frame(title="Test ImageList",size=(600,300))
panel = frame.Panel()
list = panel.ListCtrl(size=(560,240), pos=(10,10), style=wLcReport or wBorderDouble or wLcSingleSel)
var il = ImageList(size=(16,16), mask=false, initialCount=0)
il.add(blueIcon)
il.add(redIcon)
il.add(checkIcon)
list.setImageList(il)
list.appendColumn(text="Column 0", width=150)
list.setColumnFormat(0, wListFormatLeft)
list.appendColumn(text="Column 1", width=110, format=wListFormatRight)
list.appendColumn(text="Column 2", width=110, format=wListFormatRight)
list.appendColumn(text="Column 3", width=wListAutosizeUseHeader)
var imgIndex: int
for i in 0..10:
imgIndex = 1
if (i mod 2) == 0: imgIndex = 0
if (i mod 3) == 0: imgIndex = 2
list.appendItem([$i&"-0", $i&"-1", $i&"-2", $i&"-3"], imgIndex)
frame.wIdExit do ():
frame.delete()
frame.center()
frame.show()
app.mainLoop()
Alles anzeigen
Insofern kann jede Jahreszahl als valide angenommen werden. Tut keinem weh.
Ja, ok! Das Jahr könnte man weglassen (wobei die Jahre ab 1000 wohl als "gesichert" gelten, davor gab es ja einige "Meinungsverschiedenheiten" bei den Päpsten).
Aber den Monat solltest Du schon testen, denn getDaysInMonth prüft den nicht.
habt ihr keine Idee, woran das liegen könnte? Macht mich echt kirre, dass ich Nim nicht bzw. nicht mehr richtig ans Laufen bekomme.
Hast Du VSCodium über scoop installiert scoop install vscodium?
Ich habe nämlich gerade ein Update gemacht scoop update vscodium und bei mir läuft das neue VSCodium (v1.49) problemlos.
Aber mir ging es darum, dass ich von meiner dateAdd-Funktion den Rückgabetyp DateTime bekomme. Da kann ich bei interner Prüfung nicht plötzlich "false" zurückgeben. Ich muss die Fehlermeldung also im erwarteten Datentyp unterbringen.
Ich dachte an ein "Fehler-Datum" (z.B. 01.01.0001):
import times
proc dateIsValid(y, m, d: int): bool =
result = if (y in 1000..2999) and (m in 1..12) and
(d in 1..getDaysInMonth(cast[Month](m), y)): true else: false
proc dateAdd(datefunc: proc (x: int): TimeInterval{.inline, noSideEffect, gcsafe, locks: 0.},
count: int, year: int = -1, mon: int = -1, day: int = -1):
DateTime =
# [...]
if not dateIsValid(year, mon, day):
let dtError = initDateTime(cast[MonthdayRange](1), cast[Month](1), 1, 0, 0, 0, local())
return dtError
Alles anzeigen
Ja klar (geht übrigens kürzer
)
Naja, Du testest ja auch nur den Tag, aber mit getDaysInMonth geht's wirklich kürzer:
Nach 10 Minuten schon die Lösung - schau bitte ins Nim Forum.
Ok, das ist schnell und es löst das Problem.
Aber:
Zitat
Default literals are of int type.
Ok, kann ich noch nachvollziehen.
Ich denke aber, dass die Erkennung der einzelnen Datentypen dann trotzdem "fehlerhaft" ist.
Warum kann man 0x7FFFFFFF der Variablen ohne Zusatz zuweisen und ab 0x80000000 nicht mehr?
Bis 0xFFFFFFFF ist es doch eindeutig ein 32-Bit-Datentyp (ganz egal ob signed oder unsigned).
Da läuft etwas falsch!
Man kann einer uint32-Variablen keinen Wert größer als 0x7FFFFFFF zuweisen. Ab 0x80000000 kommt als Fehler: Error: type mismatch: got <int64> but expected 'uint32'
var test: uint32 = 0x7FFFFFFF # <- geht noch
test = 0x80000000 # <- geht nicht mehr Error: type mismatch: got <int64> but expected 'uint32'
BugFix : Könntest Du mal wieder einen Bugreport schreiben?
Errorhandling ist tatsächlich etwas, wo man durch AutoIt verwöhnt ist. Es gibt kein Errormakro und somit ist das Signalisieren von Fehlern recht umständlich. Aber: Man kann nicht alles haben.
Du könntest das Datum doch auch in einer eigenen Funktion überprüfen:
import times
proc dateIsValid(y, m, d: int): bool =
const aDays = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if not (y in 1000..2999): return false
if not (m in 1..12): return false
if not (d in 1..aDays[m] + (if m == 2 and isLeapYear(y): 1 else: 0)): return false
return true
echo "Date is valid: ", dateIsValid(2021, 2, 29)
Edit: Da war noch ein kleiner Fehler drin. Ich hatte das "+1" mit im Array-Index. Behoben!
Ich weiß nicht, ob NIM diese Datenstruktur überhaupt implementiert hat.
Nachdem ich jetzt ausgiebig gegoogelt habe, fand ich ein Beispiel für eine Union in Nim.
Und daraus habe ich jetzt mal ein Beispiel erstellt:
from strutils import toHex
type
ARGB* {.union.} = object
c*: tuple[b, g, r, a: uint8]
argb*: uint32
var color: ARGB
color.c.a = 0xFF
color.c.r = 0xAA
color.c.g = 0xBB
color.c.b = 0xCC
echo color.argb.toHex
color.argb = 0xFFE0D0C0'u32
echo color.argb.toHex
echo color.c.r.toHex
Alles anzeigen
Das ist etwas komplizierter und vom Zugriff her nicht so optimal, aber es funktioniert.
So nebenbei habe ich dann auch mal Union verstanden. Danke, UEZ! ![]()
![]()
Edit: Code noch etwas gekürzt. So geht's auch.
Edit2: Den Vorschlag von UEZ eingebaut. Jetzt als ARGB.
Das funktioniert soweit zufriedenstellend, nur werden die Funktionen 'weeks' und 'days' als Parameter nicht erkannt. Da habe ich keine Erklärung für.
Das liegt daran, dass die beiden Funktionen zwei Mal vorhanden sind (einmal mit TimeInterval und einmal mit Duration).
Du musst den Aufruf quasi zu dem TimeInterval zwingen:
import times, tables
# ------------------------------------------------------------------------------
type
ConditionPair[T] = object
ifTrue, ifFalse: T
# ternary operator: (cond ? if-true ! if-false)
proc `!`*[T](a, b: T): ConditionPair[T] {.inline.} =
ConditionPair[T](ifTrue: a, ifFalse: b)
template `?`*[T](cond: bool; p: ConditionPair[T]): T =
(if cond: p.ifTrue else: p.ifFalse)
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# dateFunc: The procedure to use ('years', 'months', 'weeks', 'days')
# count: The count of units to add. Negative value for subtraction.
# year: The year for the start date. -1 (default) - the current year
# mon: The month for the start date. -1 (default) - the current month
# day: The day for the start date. -1 (default) - the current day
# ------------------------------------------------------------------------------
proc dateAdd(datefunc: proc (x: int): TimeInterval{.inline, noSideEffect, gcsafe, locks: 0.}, count: int, year: int = -1,
mon: int = -1, day: int = -1): DateTime =
let dtToday = now() # date Today
let # if default (-1) - Today-value
y = year == -1 ? dtToday.year ! year
m: Month = mon == -1 ? dtToday.month ! cast[Month](mon)
d: MonthdayRange = day == -1 ? dtToday.monthday ! cast[MonthdayRange](day)
let dtStart = initDateTime(d, m, y, 0, 0, 0, local())
result = dtStart + (count.datefunc())
# ------------------------------------------------------------------------------
let dateFormat = initTimeFormat("dd'.'MM'.'yyyy")
echo "Heute: ", now().format(dateFormat)
var d = dateAdd(years, -2)
echo "Heute -2 Jahre: ", d.format(dateFormat)
d = dateAdd(years, 2)
echo "Heute +2 Jahre: ", d.format(dateFormat)
d = dateAdd(months, -3, 2016, 2, 28)
echo "28.02.2016 -3 Monate: ", d.format(dateFormat)
d = dateAdd(months, -5)
echo "Heute -5 Monate: ", d.format(dateFormat)
d = dateAdd(months, 5)
echo "Heute +5 Monate: ", d.format(dateFormat)
d = dateAdd(weeks, -3)
echo "Heute -3 Wochen: ", d.format(dateFormat)
d = dateAdd(weeks, 3)
echo "Heute +3 Wochen: ", d.format(dateFormat)
d = dateAdd(days, -21)
echo "Heute -21 Tage: ", d.format(dateFormat)
d = dateAdd(days, 14)
echo "Heute +14 Tage: ", d.format(dateFormat)
Alles anzeigen
du könntest UNION in TYPE verwenden, um r, g, b, a mit z.b. col als Int32 zu vereinen.
Würde ich versuchen, wenn ich wüsste wie...
Funktionen in Nim, um Farbwerte zwischen RGB und BGR und zwischen RGB und HSV umzuwandeln konnte ich nicht finden, also habe ich das mal selbst geschrieben.
Ich habe die Funktionen überladen, sodass man sowohl einen int32 Wert übergeben kann, als auch drei Einzelwerte für RGB bzw. HSV. Außerdem gibt es noch jeweils ein Objekt für die Beiden.
Bei RGB:
Die Werte für R, G und B liegen jeweils zwischen 0...255 (0x00...0xFF).
Bei HSV:
Der Wert für H liegt zwischen 0...359. Und die Werte für S und V zwischen 0.00...100.00
Das Modul:
import math
type
RGB* = object
r*, g*, b*: uint8
HSV* = object
h*, s*, v*: float
# Farbwerte von Red, Green, Blue (RGB) nach int32 konvertieren
proc toInt32*(r, g, b: uint8): int32 =
result = (int32(r) shl 16) or (int32(g) shl 8) or int32(b)
# toInt32 mit RGB-Object (Funktion ueberladen)
proc toInt32*(rgb: RGB): int32 =
result = toInt32(rgb.r, rgb.g, rgb.b)
# Farbwert von Red, Green, Blue (RGB) nach Blue, Green, Red (BGR) und umgekehrt
proc switchColor*(c: int32): int32 =
result = ((c and 0x00FF0000) shr 16) or (c and 0x0000FF00) or ((c and 0x000000FF) shl 16)
# switchColor mit drei Einzelwerten (Funktion ueberladen)
proc switchColor*(r, g, b: uint8): int32 =
result = switchColor(toInt32(r, g, b))
# Minimumwert ermitteln aus mehreren Floats (mit Maxwert von 1.0)
# Nur fuer modulinterne Aufgaben!
proc myMin(f: varargs[float]): float =
result = 1.0
for v in f:
if v < result: result = v
# Maximumwert ermitteln aus mehreren Floats (mit Minwert von 0.0)
# Nur fuer modulinterne Aufgaben!
proc myMax(f: varargs[float]): float =
result = 0.0
for v in f:
if v > result: result = v
# Floatwert auf zwei Nachkommastellen beschraenken
# Nur fuer modulinterne Aufgaben!
proc myRound(x: float): float =
result = floor(x * 100) / 100
# Farbwert von Red, Green, Blue (RGB) nach Hue, Saturation, Value (HSV)
# Das Ergebnis (HSV-Object) sind drei Floatwerte (mit max. zwei Nachkommastellen).
proc RGBtoHSV*(c: int32): HSV =
var r, g, b, minV, maxV, del, h: float
r = ((c and 0x00FF0000) shr 16).toFloat / 255
g = ((c and 0x0000FF00) shr 8).toFloat / 255
b = ((c and 0x000000FF)).toFloat / 255
minV = myMin(r, g, b)
maxV = myMax(r, g, b)
del = maxV - minV
result.v = myRound(maxV * 100)
if del == 0:
result.h = 0
result.s = 0
else:
result.s = myRound((del / maxV) * 100)
if maxV == r: h = 60 * (0 + (g - b) / del)
if maxV == g: h = 60 * (2 + (b - r) / del)
if maxV == b: h = 60 * (4 + (r - g) / del)
if h < 0: h += 360
result.h = myRound(h)
# RGBtoHSV mit drei Einzelwerten (Funktion ueberladen)
proc RGBtoHSV*(r, g, b: uint8): HSV =
result = RGBtoHSV(toInt32(r, g, b))
# RGBtoHSV mit RGB-Object (Funktion ueberladen)
proc RGBtoHSV*(rgb: RGB): HSV =
result = RGBtoHSV(toInt32(rgb))
# Farbwert von Hue, Saturation, Value (HSV) nach Red, Green, Blue (RGB)
# Das Ergebnis (RGB) wird als int32 ausgegeben
proc HSVtoRGB*(hsv: HSV): int32 =
var
h, s, v, f: float
iH, iV, iP, iQ, iT: uint8
res: RGB
h = hsv.h
s = hsv.s / 100
v = hsv.v / 100
f = h / 60 - floor(h / 60)
v *= 255
iH = uint8(floor(h / 60) mod 6)
iV = uint8(v)
iP = uint8(v * (1 - s))
iQ = uint8(v * (1 - f * s))
iT = uint8(v * (1 - (1 - f) * s))
case iH
of 0: res = RGB(r: iV, g: iT, b: iP)
of 1: res = RGB(r: iQ, g: iV, b: iP)
of 2: res = RGB(r: iP, g: iV, b: iT)
of 3: res = RGB(r: iP, g: iQ, b: iV)
of 4: res = RGB(r: iT, g: iP, b: iV)
else: res = RGB(r: iV, g: iP, b: iQ)
result = res.toInt32
# HSVtoRGB mit drei Einzelwerten (Funktion ueberladen)
proc HSVtoRGB*(h, s, v: float): int32 =
result = HSVtoRGB(HSV(h: h, s: s, v: v))
Alles anzeigen
Und das Beispiel:
import ColorConvert
from strutils import toHex
let
r: uint8 = 0xAA
g: uint8 = 0xBB
b: uint8 = 0xCC
let bgrColor = toInt32(b, g, r) # die drei Einzelwerte nach int32 konvertieren
echo "Color (BGR): 0x", bgrColor.toHex
let rgbColor = bgrColor.switchColor # switch BGR <-> RGB
echo "Color (RGB): 0x", rgbColor.toHex
let hsvColor = RGBtoHSV(rgbColor) # Uebergabe als int32
echo "Color (HSV): H = ", hsvColor.h, ", S = ", hsvColor.s, ", V = ", hsvColor.v
let hsvColor2 = RGBtoHSV(0xAA, 0xBB, 0xCC) # Uebergabe mit drei Einzelwerten
echo "Color (HSV): H = ", hsvColor2.h, ", S = ", hsvColor2.s, ", V = ", hsvColor2.v
let rgbColor2 = HSVtoRGB(hsvColor) # Uebergabe mit HSV-Object
echo "Color (RGB): 0x", rgbColor2.toHex
let rgbColor3 = HSVtoRGB(210, 16.66, 80) # Uebergabe mit drei Einzelwerten
echo "Color (RGB): 0x", rgbColor3.toHex
Alles anzeigen
Alles auch zum downloaden im Anhang.