Moskus Skrevet 8. desember 2006 Skrevet 8. desember 2006 Hei! Jeg holder på å lage en makro i Excel som beregner støynivå i rommet ut fra gitte kilder og absorpsjonsegenskaper til rommet. Veldig spennende! Men jeg er lei av VBA, og vil gjerne ha det over i .NET. Men da må jeg ha en fornuftig måte å presentere det på. Jeg får ut data i tabellform som vist under (klikk for større): Og med Excel sin "Surface" grafe, kan man lage et fint oversiktsbilde (selv om Excel ikke akkurat velger logiske farger som gir glidende overganger) slik: Gitt matrisen over, er det mulig å lage noe slikt i .NET? Noen som vet hvordan? På forhånd takk for hjelpen!
j000rn Skrevet 8. desember 2006 Skrevet 8. desember 2006 (endret) GDI+ System.Drawing.Image/Bitmap Se GeirGrusom sitt eksempel på effektiv tegning: https://www.diskusjon.no/index.php?showtopic=648798 Endret 8. desember 2006 av jorn79
Moskus Skrevet 8. desember 2006 Forfatter Skrevet 8. desember 2006 Det er greit å bruke GDI+ på et Image/Bitmap, men hvordan... Som du ser dekker hver celle (i det øverste bildet) mer enn en pixel i det nederste. Excel genererer på en måte høydekurver mellom de aktuelle data. Det er det store problemet.
j000rn Skrevet 9. desember 2006 Skrevet 9. desember 2006 Denne er ikke rask. Men skal være ganske enkel å forså ihvertfall class Program { static void Main(string[] args) { int[,] Orginal = new int[10, 10]; int[,] Bigger = new int[20, 20]; // Lager random data i Original Random r = new Random(); for (int y = 0; y <= Orginal.GetUpperBound(1); y++) for (int x = 0; x <= Orginal.GetUpperBound(0); x++) Orginal[x, y] = r.Next(0, 99); // Kopierer fra Origianl over til Bigger ResizeAndInterpolate(Orginal, Bigger); // Debug :) Print(Orginal); Print(Bigger); Console.ReadKey(); } private static void ResizeAndInterpolate(int[,] org, int[,] big) { for (int y = 0; y <= big.GetUpperBound(1); y++) { // Original Y koordinat double OrgY = (double)org.GetUpperBound(1) * (double)y / (double)big.GetUpperBound(1); // Øverste og nederste Y verdi int Y1 = (int)Math.Floor(OrgY); int Y2 = (int)Math.Ceiling(OrgY); // DeltaY (alltid mellom 0..1) double DY = OrgY - Y1; // Eks: // OrgY = 5.1 ==> // Y1 = 5, Y2=6, DY = 0.1 for (int x = 0; x <= big.GetUpperBound(0); x++) { // Samma stæsj for X double OrgX = (double)org.GetUpperBound(0) * (double)x / (double)big.GetUpperBound(0); int X1 = (int)Math.Floor(OrgX); int X2 = (int)Math.Ceiling(OrgX); double DX = OrgX - X1; // Nå har vi 4 koordinater (X1,Y1 - X1,Y2 - X2,Y1 - X2,Y2) // og forholdet mellom disse (DX,DY) // Tallverdiene for Y på høyre og venstre side. double ValueY1 = org[X1, Y1] + (org[X1, Y2] - org[X1, Y1]) * DY; double ValueY2 = org[X2, Y1] + (org[X2, Y2] - org[X2, Y1]) * DY; // Interpolerer igjen for å finne Tallverdien mellom høyre og venstre side. double Value = ValueY1 + (ValueY2 - ValueY1) * DX; // :D big[x, y] = (int)Value; } } } private static void Print(int[,] box) { for (int y = 0; y <= box.GetUpperBound(1); y++) { for (int x = 0; x <= box.GetUpperBound(0); x++) Console.Write(box[x, y].ToString("00") + " "); Console.WriteLine(); } } }
Moskus Skrevet 11. desember 2006 Forfatter Skrevet 11. desember 2006 Skulle ønske jeg var smart slik at jeg øyeblikkelig kunne forstå hva de rutinene der gjør... Men jeg tror jeg ser hva du gjør. Forstår jeg det riktig om jeg sier at den tar en matrise, lager en større matrise og interpolerer aritmetisk for å fylle "tomrommet"? Ganske lurt! Da er det bare å lage en ny matrise basert på den gamle skalere denne slik at alle tall er mellom 0 og 1. Så er det bare å gange med en ColorMatrix Da burde det være en kjapp sak å bygge den om til interpolering med logaritmiske verdier!
j000rn Skrevet 11. desember 2006 Skrevet 11. desember 2006 Skulle ønske jeg var smart slik at jeg øyeblikkelig kunne forstå hva de rutinene der gjør... Men jeg tror jeg ser hva du gjør. Forstår jeg det riktig om jeg sier at den tar en matrise, lager en større matrise og interpolerer aritmetisk for å fylle "tomrommet"? Ganske lurt! Da er det bare å lage en ny matrise basert på den gamle skalere denne slik at alle tall er mellom 0 og 1. Så er det bare å gange med en ColorMatrix Da burde det være en kjapp sak å bygge den om til interpolering med logaritmiske verdier! 7474588[/snapback] Jeg lagde den slik at den skulle bli enkel å forstå. Jeg ville gjordt ting litt anderledes hvis hastighet skulle vært viktig. Når det gjelder å mappe verdiene til farge så hørtes det veldig tungvindt ut å bruke ColorMatrix saken... ... ResizeAndInterpolate(Original, Bigger); // Lager fargekart... Color[] FargeVerdi = new Color[MaxVerdi]; for(int i = 0; i < MaxVerdi; i++) FargeVerdi = Color.FromArgb( i * 255 / MaxVerdi,0,0); // Kan tegne mer fancy farger, men her mappes alle fargene bare fra sort til rød. // Tegner stæsj over på et bilde... for(int y = 0; y < Bigger.GetBounds(0); y++) for(int x = 0; x < Bigger.GetBounds(0); x++) g.PutPixel(x,y, FargeVerdi[ Bigger[x,y] ] );
Moskus Skrevet 11. desember 2006 Forfatter Skrevet 11. desember 2006 Da kan jeg spørre: Hva ville du gjort om hastighet var viktig? Hvis jeg nå bare hadde fått DataGridView til å akseptere data jeg la inn, så hadde jeg snart kommet noen vei. Hvorfor skal ting være så mye vanskeligere enn i VS 2003?!?
j000rn Skrevet 11. desember 2006 Skrevet 11. desember 2006 Da kan jeg spørre: Hva ville du gjort om hastighet var viktig? 7476600[/snapback] Ikke regnet ut noe i innerloop'en. Stigningstallet kan regnes ut på utsiden, og heller bare plusses på for hver X verdi. Dvs. at innerloop'en ville kun inneholdt + istedenfor all koden med mye / og * som det står nå. Hvis du skal ha det enda raskere så kan også dette optimaliseres med unsafe og unchecked kode... I tillegg ville jeg ikke brukt putpixel for å tegne den, men det forstod du vel Se linken til GeirGrusom sin post...
Moskus Skrevet 11. desember 2006 Forfatter Skrevet 11. desember 2006 Ikke regnet ut noe i innerloop'en. Stigningstallet kan regnes ut på utsiden, og heller bare plusses på for hver X verdi. Dvs. at innerloop'en ville kun inneholdt + istedenfor all koden med mye / og * som det står nå. 7476663[/snapback] Selvfølgelig! Takker så meget!
Moskus Skrevet 12. desember 2006 Forfatter Skrevet 12. desember 2006 Allright! Applikasjonen regner ut verdier, og gir fornuftige svar (et par småbugs som gir lydnivå på utsiden av rommet, men det henger med til mine testdata, så jeg tar det ikke så alvorlig siden lydnivået stemmer inni rommet). Og igjen takk til Jorn som gav meg den hendige rutinen for interpoleringen. MEN (og det er jo forbanna alltid et men): I første omgang vil jeg ha dette til å fungere: g.PutPixel(x,y, FargeVerdi[ Bigger[x,y] ]) ... finnes ikke i VB. Jeg vil bare enkelt og greit tegne en pixel. Umulig kan det jo ikke være...
j000rn Skrevet 12. desember 2006 Skrevet 12. desember 2006 dim b as new System.Drawing.Bitmap(Bigger.GetBounds(1), Bigger.GetBounds(0)); dim g as System.Drawing.Graphics = b.CreateGraphics(); for y as integer = 0 to Bigger.GetBounds(0) for x as integer = 0 to Bigger.GetBounds(1) g.PutPixel(?) next next sånn ca.... må løpe nå, men lykke til!
GeirGrusom Skrevet 12. desember 2006 Skrevet 12. desember 2006 Det er ikke noe PutPixel i Graphics vel... Bitmap.SetPixel() er svaret på gåten tenker jeg
Moskus Skrevet 12. desember 2006 Forfatter Skrevet 12. desember 2006 Bitmap.SetPixel gjorde susen! Jeg trodde den lå under Graphics...
Moskus Skrevet 15. desember 2006 Forfatter Skrevet 15. desember 2006 Takk for all hjelp til mine to favoritt .NET guruer! Jeg poster et skjermskudd "just for fun". Fremdeles mye arbeid igjen, men det fungerer!
GeirGrusom Skrevet 15. desember 2006 Skrevet 15. desember 2006 Ekstremt fancy... Noe a det mest fancy jeg har sett på lenge. Men jeg frykter at SetPixel er en ekstremt treg metode, går det fort å tegne?
Moskus Skrevet 15. desember 2006 Forfatter Skrevet 15. desember 2006 Takker og bukker! Når det kommer fra deg så betyr det en del! SetPixel er vel kanskje ikke det kjappeste, nei. "Mottakermatrisen" er 4 ganger større enn største rom-dimensjoni meter (dvs. oppløsning = 0.25 ). Videre bruker jeg Jørns algoritme (mer eller mindre ukritisk) for å lage et større bilde for å få frem "diffuseringen" bedre (mattrisens dimensjoner er 20 ganger større enn "mottakermatrisen"). Jeg kjørte en "ekstrem" beregning med med et rom på 72x51x7.5 meter. Da tar selve beregningen ca. 16 minutter (mot ca. 2:30) og tegningen av bildet tar ca. 1 minutt (mot maksimalt et par sekunder). Men da er jo grafen riktignok på 33.177.600 pixler (72*4*20) så det er jo forståelig at det tar litt tid... Men kommer du på noe som kan gjøre det kjappere, så tar jeg veldig gjerne i mot! Jeg har enda ikke sett på hvilken oppløsning som faktisk er nødvendig, eller prøvd å optimalisere Jørns funksjon. Foreløpig har fokuset ligget på å få det til å kjøre skikkelig. Et lite screenshot av det "store" rommet (klikk for større). Jeg tror skjermen gir bittelitt "hardere" linjer enn man har i virkeligheten, så modellen min må nok justeres litt. Men for "små" rom kan det se ut som om det stemmer ganske bra!
GeirGrusom Skrevet 15. desember 2006 Skrevet 15. desember 2006 (endret) Vel, jeg vet ikke om dette går raskere, men jeg refererer til koden som jeg og jorn fikset i C# delen, dette er i C#, og funker bare i C#, men du kan eventuelt lage en dll: using System; using System.Drawing; using System.Drawing.Imaging; namespace SharpGLTest { public delegate Color QuickDrawProc(int x, int y); class QuickDraw { protected Bitmap m_bmp; protected bool m_locked; public event QuickDrawProc ProcessPixel; protected BitmapData m_data; public unsafe void Draw() { int* ptr; LockBuffer(); ptr = (int*)m_data.Scan0.ToPointer(); for (int y = 0; y < m_data.Height; y++) { for (int x = 0; x < m_data.Width; x++, ptr++) { *ptr = ProcessPixel(x, y).ToArgb(); } } UnlockBuffer(); } public Bitmap Bitmap { get { return m_bmp; } } public QuickDraw(Bitmap bmp) { if (!((bmp.PixelFormat == PixelFormat.Format32bppArgb) || (bmp.PixelFormat == PixelFormat.Format32bppRgb))) throw new Exception("Only 32-bit pixelformats are supported."); m_locked = false; m_bmp = bmp; } public bool Locked { get { return m_locked; } } public void LockBuffer() { if (!m_locked) { m_data = m_bmp.LockBits(new Rectangle(0, 0, m_bmp.Size.Width, m_bmp.Size.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, m_bmp.PixelFormat); m_locked = true; } } public void UnlockBuffer() { if (m_locked) { m_bmp.UnlockBits(m_data); m_locked = false; } } public unsafe void SetPixel(int x, int y, Color c) { if (m_locked) { int* ptr = (int*)m_data.Scan0.ToPointer(); ptr += y * m_data.Width + x; *ptr = c.ToArgb(); } } } } Det som er spesielt, er at den bruker pekere for å sette data, pluss at den kun støtter 32-bit, og burde gå ganske raskt. Den kan brukes på to måter: Enten legg inn en event handler på ProcessPixel, eller bruk SetPixel funksjonen i klassen. Du kan prøve, vet ikke om det går noe raskere, men inntrykket jeg har av dette, er at det går svinaktig mye raskere en tilsvarende med Bitmap.SetPixel Edit: Du må kalle QuickDraw.LockBuffer() før du kan bruke SetPixel funksjonen, og QuickDraw.UnlockBuffer() før du kan bruke Bitmap egenskapen, for så lenge bitmapen er låst, kan ikke GDI+ bruke den. Endret 15. desember 2006 av GeirGrusom
GeirGrusom Skrevet 16. desember 2006 Skrevet 16. desember 2006 Hehe, laget et eksempel: public partial class frmColorTopia : Form { private volatile bool m_app_running; private volatile bool m_app_stopped; private PictureBox pctDisp; QuickDraw quick_draw; Bitmap bmp; int w2; int h2; double degrees_x; double degrees_y; public frmColorTopia() { this.Update(); this.Show(); Application.DoEvents(); //InitializeComponent(); pctDisp = new PictureBox(); pctDisp.Dock = DockStyle.Fill; pctDisp.Paint += new PaintEventHandler(Form1_Paint); this.Text = "ColorTopia"; Controls.Add(pctDisp); bmp = new Bitmap(256, 256); w2 = bmp.Size.Width / 2; h2 = bmp.Size.Height / 2; degrees_x = Math.PI / w2; degrees_y = Math.PI / h2; quick_draw = new QuickDraw(bmp); quick_draw.ProcessPixel += new QuickDrawProc(quick_draw_ProcessPixel); m_app_stopped = false; RunApp(); quick_draw.UnlockBuffer(); } int xx = 1; int yy = 256; Color quick_draw_ProcessPixel(int x, int y) { /*double fx = (Math.Cos(x * degrees_x) * Math.Sin(xx * degrees_x)) * 127 + 127; double fy = (Math.Cos(y * degrees_y) * Math.Cos(yy * degrees_y)) * 127 + 127; double fz = (Math.Cos(x * degrees_x) * Math.Cos(xx * degrees_x))* 127 + 127; return Color.FromArgb((int) fx, (int)fy, (int)fz);*/ return Color.FromArgb(((x + 1) & (y + 1)) % 256, ((xx + x) & (yy + y)) % 256, ((yy + x) & (yy + y)) % 256); } public void RunApp() { m_app_running = true; while (m_app_running) { Application.DoEvents(); xx++; yy--; if (yy < 0) yy = 256; if(xx > 255) xx = 0; quick_draw.Draw(); pctDisp.Invalidate(); } m_app_stopped = true; } private void Form1_Load(object sender, EventArgs e) { } private void Form1_Paint(object sender, PaintEventArgs e) { bool was_locked = false; if (quick_draw.Locked) { quick_draw.UnlockBuffer(); was_locked = true; } e.Graphics.DrawImage(bmp, 0, 0); if (was_locked) quick_draw.LockBuffer(); } private void frmColorTopia_FormClosing(object sender, FormClosingEventArgs e) { } }
Moskus Skrevet 18. desember 2006 Forfatter Skrevet 18. desember 2006 Nydelig! (Planen var å prøve dette i helga, men sykdom, julebord og "dagen-derpå" satte en effektiv stopper for det) Av generell nysgjerrighet: Har er det som gjør at dette ikke fungerer i VB.NET?
Anbefalte innlegg
Opprett en konto eller logg inn for å kommentere
Du må være et medlem for å kunne skrive en kommentar
Opprett konto
Det er enkelt å melde seg inn for å starte en ny konto!
Start en kontoLogg inn
Har du allerede en konto? Logg inn her.
Logg inn nå