Permalänk
Medlem

Generera färgspektrum

Lång historia kort: jag vill få ett färgspektrum som jag kan använda för att få en "heat map". Det ska alltså vara ett kontinuerligt spektra där blått motsvarar 0% och rött 100%. Jag har dock nära noll erfarenhet med grafik i allmänhet och har kört fast totalt när det gäller att mappa det här till något sorts färgvärde (förslagsvis RGB). Jag har letat efter pseudokod utan att hitta något och förmodligen är det inte så svårt att fixa, men det är ju inte mycket till tröst när man inte kommer på det. Hur skulle man t.ex. skriva en loop för att stegvis generera RGB-motsvarigheten för färger från blått->grönt->gult->rött som på bilden?

Permalänk
Medlem
Skrivet av Thomas H:

Lång historia kort: jag vill få ett färgspektrum som jag kan använda för att få en "heat map". Det ska alltså vara ett kontinuerligt spektra där blått motsvarar 0% och rött 100%. Jag har dock nära noll erfarenhet med grafik i allmänhet och har kört fast totalt när det gäller att mappa det här till något sorts färgvärde (förslagsvis RGB). Jag har letat efter pseudokod utan att hitta något och förmodligen är det inte så svårt att fixa, men det är ju inte mycket till tröst när man inte kommer på det. Hur skulle man t.ex. skriva en loop för att stegvis generera RGB-motsvarigheten för färger från blått->grönt->gult->rött som på bilden?

Ta nu mitt svar med en nypa salt, det var typ 10 år sedan jag programmerade nåt annat än gammaldags ASP kod så jag ska inte försöka ge dig ett superbra svar, men mitt bästa kanske?

En stegare som börjar på RGB(0, 0, 0). Går till 0, 0, 1 -> 0, 0, 2 -> 0, 0, 3 ända fram till RGB(255, 255, 255)
Och mellan varje ändring ritar du ut en pixel på skärmen eller sparar det i en array?

Sen tror jag att de mer kunniga programmerarna kan hjälpa dig mer specifikt om du berättar vilket språk du ska använda dig av, samt vilken utvecklingsmiljö.

Hoppas nån annan kastar fram ett mer specifikt svar än mig

Visa signatur

5700x3D | RTX 2060 Super | 2 TB M.2 | 32 GB RAM | Gigabyte DS3H| 750 WATT

Permalänk
Glömsk

Så bilden du länkade till är indelad i 6 segment med 5 "väggar" imellan. De grundläggande färgkomponeterna är centrerade på på vägg 0 (röd), 2 (grön) och 4 (blå).

Så om du skapar en array sedan adderar du ihop så får du ditt spektrum. RGB är additiv skala så det är bara att ha en array och hela tiden addera till den.

Kan rekommendera att du använder flyttal för färgerna så RGB(1.0,1.0,1.0) är helt vit sedan kan du normalisera i slutet till integers från 0-255.

Så låt säga att du vill att ditt spektrum är 600 px brett. Den gröna komponenten är då centrerad på px 200 och ska gå från maximal grönt RGB(0,1.0,0) till svart vid px 400 (och även åt andra hållet, till px 200).

Så den här lösningen går alltså ut på att förgenerera att spektrum och sedan använda det för att få ut RGB-värdena du vill ha. Finns andra lösningar, men denna är enklast.

Visa signatur

...man is not free unless government is limited. There's a clear cause and effect here that is as neat and predictable as a law of physics: As government expands, liberty contracts.

Permalänk
Medlem

Det är inte själva ritandet som är problemet utan hur man ska stega igenom färgerna. Ta den här koden i Java t.ex.:

public void paint(Graphics go) { //blue->aqua for (int g = 0; g <= 255; g++) { go.setColor(new Color(0,g,255)); go.drawLine(g+5, 0, g+5, 50); } //aqua->green for (int b = 255; b > 0; b--) { go.setColor(new Color(0,255,b)); go.drawLine((255-b)+260, 0, (255-b)+260, 50); } //green->yellow for (int r = 0; r <= 255; r++) { go.setColor(new Color(r,255,0)); go.drawLine(r+515, 0, r+515, 50); } //yellow->red for (int g = 255; g > 0; g--) { go.setColor(new Color(255,g,0)); go.drawLine((255-g)+770, 0, (255-g)+770, 50); } }

Lägg det där i en canvas som är 1035*50 i storlek så bör det ge en trevilgt spektrum (även om jag misstänker att man skulle kunna få den lite mer varierad inom färgerna om man kör med alpha också, men det ligger utanför min förmåga). Visserligen kan jag ju göra en array som innehåller färgerna på de platsen som mostvarar pixeln i X-led som den där koden ritar på, men jag skulle vilja slippa lagra undan färgerna. Jag skulle behöva slå ihop de där looparna så att man får någon sorts linjär utveckling och sedan kan förutsäga hur man ska blanda komponenterna för t.ex. 50% värme (som då skulle vara grönt).

Permalänk

Det här är ungefär vad du är ute efter. Att gå från mörkblått till Mörkrött.

%[ 0 0,15 0,3 0,4 0,55 0,75 1,0] R[ 0 0 0 0 255 255 64] G[ 0 0 255 255 255 0 0] B[ 128 255 255 0 0 0 0] [XXXX XXXX XXXX XXXX XXXX XXXX XXXX]

Din indata är ett värde som varierar mellan 0,0 och 1,0.

Vill du generera ett färgvärde till 0.1% så ger du den värdet 0 för rött, 0 för grön och en interpolation mellan 128 och 255 för blått.

Vill du ha ett värde för 0,45% så interpolerar du mellan 0 och 255 för rött, 255 för blått och 0 för rött.

Resultatet blir väldigt likt den färgskala som används i matlab och det ser minst lika bra ut.

Så, vad är den lite längre historien. Vad ska du ha det till?

Permalänk
Medlem

Nja, så lång är väl inte historien, men jag försöker att rita några grafer. Jag fick slut på dimensioner en gång när jag skulle rita några grafer för nätverksgrejer och i brist på idéer för hur man skulle rita 4D grafer så tänkte jag att man skulle kunna färgkoda en dimension istället. Det skulle alltså bli ett 3D plan med skiftande färg (det slutade den gången med att jag fick dela upp det i flera diagram i gnuplot). Jag vet inte hur bra det skulle bli och jag har inte fått till ens 3D representation än (finns ju dock formler för hur man projicerar 3D koordinater till 2D), men jag tänkte göra en enkel prototyp med färg först för att se om det ens är värt att fortsätta. Det är mest för skojs skull, jag har ingen omedelbar plan för vad det ska användas till.

Den där tabellen beskriver vad jag vill, men vad jag funderar över är hur man skulle kunna få de olika procenten att motsvara en färg utan att lagra någon data. Alltså, finns det någon formel som r(x) = ..., g(x) = ..., b(x) = ... för att räkna ut komponenterna för ett givet värde av X (som då motsvarar procent av värmen)?
Jag antar att man kan slänga in ett gäng if-satser utifrån tabellen som gör en formel för t.ex. röd där x<=0,4 ger 0, 0,4<x<=0,55 ger en stegvis ökande röd komponent beroende på vad X är, 0,55<x<=0,75 ger 255, och 0,75<x<=1 ger en stegvis minskande komponent. Känns lite omständigt, men det kanske inte finns något enklare sätt.

Permalänk
Medlem

Inte helt hundra på vad du försöker åstadkomma, men det kanske är lättare att arbeta med HSL/HSV istället?

http://en.wikipedia.org/wiki/HSL_and_HSV
http://www.otherwise.com/Lessons/ColorsInJava.html

Om du vill mappa en dimension av din data till färg via HSL kan du låta saturation vara på 100% och brightness på 50% och styra färg via hue. Eftersom kulör endast styrs genom hue så blir det lättare att skapa en loop för.

Visa signatur

data, representation av värden, text etc. lämpad för överföring, tolkning eller bearbetning av människor eller maskiner.
dator, digital automatisk beräkningsmaskin som styrs av ett i dess minne lagrat program.

Permalänk
Skrivet av Thomas H:

Nja, så lång är väl inte historien, men jag försöker att rita några grafer. Jag fick slut på dimensioner en gång när jag skulle rita några grafer för nätverksgrejer och i brist på idéer för hur man skulle rita 4D grafer så tänkte jag att man skulle kunna färgkoda en dimension istället. Det skulle alltså bli ett 3D plan med skiftande färg (det slutade den gången med att jag fick dela upp det i flera diagram i gnuplot). Jag vet inte hur bra det skulle bli och jag har inte fått till ens 3D representation än (finns ju dock formler för hur man projicerar 3D koordinater till 2D), men jag tänkte göra en enkel prototyp med färg först för att se om det ens är värt att fortsätta. Det är mest för skojs skull, jag har ingen omedelbar plan för vad det ska användas till.

Den där tabellen beskriver vad jag vill, men vad jag funderar över är hur man skulle kunna få de olika procenten att motsvara en färg utan att lagra någon data. Alltså, finns det någon formel som r(x) = ..., g(x) = ..., b(x) = ... för att räkna ut komponenterna för ett givet värde av X (som då motsvarar procent av värmen)?
Jag antar att man kan slänga in ett gäng if-satser utifrån tabellen som gör en formel för t.ex. röd där x<=0,4 ger 0, 0,4<x<=0,55 ger en stegvis ökande röd komponent beroende på vad X är, 0,55<x<=0,75 ger 255, och 0,75<x<=1 ger en stegvis minskande komponent. Känns lite omständigt, men det kanske inte finns något enklare sätt.

Vill du ha det riktigt simpelt så kan du använda gråskala istället.

Vill du ha det i färg så blir det mer komplicerat eftersom olika färger upplevs olika. Precis som du antar så är det lättast att implementera det med if-satser. Tabellen är bara som referens för att underlätta programmeringen.

Edit:
Så här ser färgkartan ut för övrigt.

Permalänk
Medlem
Skrivet av Strepto:

Inte helt hundra på vad du försöker åstadkomma, men det kanske är lättare att arbeta med HSL/HSV istället?

http://en.wikipedia.org/wiki/HSL_and_HSV
http://www.otherwise.com/Lessons/ColorsInJava.html

Om du vill mappa en dimension av din data till färg via HSL kan du låta saturation vara på 100% och brightness på 50% och styra färg via hue. Eftersom kulör endast styrs genom hue så blir det lättare att skapa en loop för.

Jo, jag kollade lite på HSV (eller HSB), men blir det inte samma problem fast med brightness istället? Om man inte vill ha en mörkare blå och röd för kanterna så går det ju vägen att bara göra så här (anpassat för en 1000-pixel canvas och blå->röd spektrum):

for (int i = 0; i <= 1000; i++) { float hue = (0.6666f - ((float)i/1000*0.6666f)) % 1; go.setColor(Color.getHSBColor(hue, 1f, 1f)); go.drawLine(i, 0, i, 50); }

Vill man däremot ha mörkblå och mörkröd måste ju även brightness variera från 1 till 0.5, men endast när due har en hue som är 0.6666 (#0000FF, blå) eller 0.3333 (#FF0000, röd). Det kanske blir lite enklare att göra specialfall än med RGB dock...

Permalänk
Medlem
Skrivet av Korkskruv:

Vill du ha det riktigt simpelt så kan du använda gråskala istället.

Vill du ha det i färg så blir det mer komplicerat eftersom olika färger upplevs olika. Precis som du antar så är det lättast att implementera det med if-satser. Tabellen är bara som referens för att underlätta programmeringen.

Jag har prövat gråskala, och det fungerar genom att använda svart (#000000), och låta alpha-värdet variera mellan 0 och 255. Det blir dock väldigt svårt att se skillnaden när man kommer över en viss alpha, så jag tänkte att olika färgnyanser kanske skulle bli tydligare.

Permalänk
Skrivet av Thomas H:

Jag har prövat gråskala, och det fungerar genom att använda svart (#000000), och låta alpha-värdet variera mellan 0 och 255. Det blir dock väldigt svårt att se skillnaden när man kommer över en viss alpha, så jag tänkte att olika färgnyanser kanske skulle bli tydligare.

Vet inte riktigt vad alpha innebär men vill du bara ha gråskala så ska du gå mellan (#000000) och (#FFFFFF). Eller snarare eftersom alla färgkomponenter alltid har samma värde vid gråskala så behöver du bara interpolera mellan 0 och 255 (#00 och #FF).

float f = värde 0.0 till 1.0 unsigned char color = 255 * f unsigned char red = color unsigned char green = color unsigned char blue = color

Men som du säger så är det förstås mycket lättare att se fler nivåer när man har fler färger inblandade.

Permalänk
Medlem

Jag gör prototypen i Java, och mitt första försök att få gråskala gick ut på att använda Color klassen med konstruktorn Color(x,x,x) där X är samtliga färger och varierade från 255 (vitt) till 0 (svart), men det gav bara helsvart när jag gjorde så. Det slutade med att jag använde en vit bakgrund och en annan konstruktor för Color klassen som använder en röd, grön och blå komponent som ovan, samt en alpha-komponent som representerar hur solid färgen ska vara. Jag använde då alltså Color(0,0,0,x), där X är alpha-komponenten som fick variera beroende på hur mörk jag ville att färgen skulle vara (går också att använda andra färger än svart, men det blev inte tydligare). Jag vet inte om det är så man ska göra egentligen, men det var det enda jag lyckades få till.

Permalänk
Medlem

Fick bli en implementation av tabellen med lite ökning av den mörkaste röda färget i alla fall. Hastigt kodad och lite för många "magic numbers", men det fungerar någorlunda, så om någon är intresserad:

import java.awt.Color; public class Spectrum { /* %[ 0 0,15 0,3 0,4 0,55 0,75 1,0] R[ 0 0 0 0 255 255 128] G[ 0 0 255 255 255 0 0] B[ 128 255 255 0 0 0 0] */ public static int getRed(double x) { if (x >= 0 && x <= 0.4) return 0; else if (x > 0.4 && x < 0.55) return (int)Math.round(255*(x-0.4)/0.15); else if (x >= 0.55 && x <= 0.75) return 255; else if (x > 0.75 && x <= 1) return (int)Math.round((255-128)*(1-x)/0.25)+128; throw new IllegalArgumentException(); } public static int getGreen(double x) { if (x >= 0 && x <= 0.15) return 0; else if (x > 0.15 && x < 0.3) return (int)Math.round(255*(x-0.15)/0.15); else if (x >= 0.3 && x <= 0.55) return 255; else if (x > 0.55 && x < 0.75) return (int)Math.round(255*(0.75-x)/0.2); else if (x >= 0.75 && x <= 1) return 0; throw new IllegalArgumentException(); } public static int getBlue(double x) { if (x >= 0 && x < 0.15) return (int)Math.round((255-128)*x/0.15)+128; else if (x >= 0.15 && x <= 0.3) return 255; else if (x > 0.3 && x < 0.4) return (int)Math.round(255*(0.4-x)/0.1); else if (x >= 0.4 && x <= 1) return 0; throw new IllegalArgumentException(); } public static Color getColor(double x) throws IllegalArgumentException { return new Color(getRed(x), getGreen(x), getBlue(x)); } }

Och för att visa spektrumet på en 1000x50 canvas:

public void paint(Graphics go) { for (int i = 0; i <= 1000; i++) { go.setColor(Spectrum.getColor((double)i/1000)); go.drawLine(i, 0, i, 50); } }

Fixade till lite exceptionhantering...
Permalänk
Medlem

Tack för hjälpen, förresten.

Permalänk
Skrivet av Thomas H:

Tack för hjälpen, förresten.

Varsågod.

Du borde kunna förbättra koden genom att utnyttja "else if" lite bättre.
Notis, korrigeringen nedan tar inte hänsyn till felaktig indata men det kan man lätt lägga till i början av metoden.

public static int getRed(double x) { if (x >= 0 && x <= 0.4) return 0; else if (x > 0.4 && x < 0.55) return (int)Math.round(255*(x-0.4)/0.15); else if (x >= 0.55 && x <= 0.75) return 255; else if (x > 0.75 && x <= 1) return (int)Math.round((255-128)*(1-x)/0.25)+128; throw new IllegalArgumentException(); }

public static int getRed(double x) { if (x <= 0.4) return 0; else if (x < 0.55) return (int)Math.round(255*(x-0.4)/0.15); else if (x <= 0.75) return 255; else return (int)Math.round((255-128)*(1-x)/0.25)+128; throw new IllegalArgumentException(); }

Permalänk
Medlem

Som sagt, det var ganska hastigt skrivet bara för att testa och jag lekte lite med intervallen för att se vad som hände så att det inte var helt kontinuerligt för alla färger, därav de dubbla vilkoren. Sen när jag kopierade in koden här så märkte jag i efterhand att jag inte hade kontroller där jag borde, så då slängde jag in några exceptions, och så klagade metoden över att det inte fanns något returvärde o.s.v... Så det fick bli en snabbfix för att hålla Eclipse nöjt och så att om någon skulle få för sig att använda koden så skulle den i alla fungera någorlunda. Tänkte ändå skriva om det vid senare tillfälle med lite mer dynamiskt justerbara värden, flyttalskomponenter istället för 0-255, och utan kryptiska siffor. Sen ska det nog portas till C++ också, men det är ett senare bekymmer.

Permalänk
Medlem

Den bästa lösningen och definitivt mest praktiska vore att helt enkelt skapa en bild på gradienten du vill ha i Photoshop eller liknande program och läsa in pixlarna vid uppstart. Enklare blir det inte, och upptäcker du att din gradient inte blir så bra är det bara att leka fram en ny och spara om.

Visa signatur

www.filipsprogram.tk - lite freeware
"Delight, herregud. Talang är bara förnamnet."