Algoritmus zur Elementevergrößerung

ctfwp444

Newbie
Registriert
Feb. 2010
Beiträge
6
Ich habe ein Problem. Langes rumüberlegen hat nichts gebracht.
Ich habe ein Schachfeld mit n Zeilen und m Spalten. Wenn ich jetzt über einem Feld bin soll dieses Feld, abhängig davon wie viele Zeilen und Spalten es insgesamt gibt, größer werden (aber nicht größer als max (konstant) ). Die Felder daneben sollen bisschen kleine sein. Und die Felder ab einer bestimmten Entfernung minimal (konstant) werden.
Z.B. die Taskleiste bei Mac.
Es würde mir ausreichen wenn einer eine Idee hat wie man das zum min. 1-Dimensional lösen könnte.

Danke im Voraus.
 
Angenommen du teilst dein Feld so ein, dass jedes Element auf einem Raster mit dem Abstand 1 liegt (passiert ja automatisch bei Indexierung). So kannst du für jedes Feld den Abstand zum ausgewählten Feld berechnen und anhand dieses Abstands die Grösse festlegen.
Code:
  1 2 3 4 5 6 7 8 9 ->m
1
2
3       X
4 
5         S 
6
7
8
9
|
V
n

S ist das ausgewählte Feld
X ist eines der anderen Felder

Der Abstand zwischen den beiden errechnet sich dann wie folgt:
d = sqrt( (Sm-Xm)^2 + (Sn-Xn)^2 )

Und anhand dieses Abstands kannst du die Skalierung festlegen. (Falls es da Performance-Probleme gibt, kannst du testweise statt des euklidischen Abstands z.B. auch den Manhatten-Abstand berechnen.)
 
Danke für die schnelle Antwort.
Aber was ich vergessen habe zu sagen. Sorry.
Das ganze "Schachbrett" hat eine konstante Größe.
Das heist die Felder zwieschen dem, das ich anklicke (mit max. Größe) und denen die "weit" entfernt sind (mit min. Größe) , müssen sich so verkleinern, dass die Summe aller Längen bzw Höhen konstant bleibt.
 
Hast du da eine Art Flow-Layout, worin sich die Elemente der Reihe nach einordnen oder wie? Ist das so gewollt oder könnte man das auch anders lösen - z.B. mit einer absoluten Positionierung der Felder?

Welche Technologien setzt du ein?
 
Eigentlich will ich einen eigenen Layout jetzt schreiben der das alles selber macht. Das heist: Ich habe ein Panel. Ich füge mehrere "Elemente" hinzu. Wenn es sehr viele sind passen sie nicht auf die Panel (Es soll ja alles übersichtlich sein). Und jetzt sollen die anderen elemente verkleinert werden (bis auf 2 px wenn es nötig ist, also wenn es sehr viele Elemente sind, was durchaus vorkommen wird) und das Element auf dem ich gerade mit der Maus bin soll groß werden.

Ach so. es sind immr die gleichen Elemente.


Das große Problem ist:

- Maus
___________________|____________
||--|--|--|--|--|--|---|----|-----|----|---|--|--||

Wenn ich jetzt die Maus nach links bewege,

__________________|_____________
||--|--|--|--|--|--|---|----|-----|----|---|--|--||
passiert erst ein Mal nichts, bis ich nächstes Element erreicht habe.

Dann aber wenn ich das nächste Element erreiche,
_________________|______________
||--|--|--|--|--|---|----|-----|----|---|--|--|--||
kommt es zur verschiebung.

Das ganze wird noch erschwert durch Verhalten an den Rändern.
Die Gesamtlänge bleibt ja konstant. Dewegen ist die "Längenabnahme" kleiner.
___|____________________________
||-----|----|----|---|---|--|--|--|--|--|--|--|--||

Ich habe versucht eine formel herzuleiten, dass das ganze beschreibt. Habs leider nicht geschaft :( .
 
Zuletzt bearbeitet:
Klingt nett. :)

Für eine Dimension stelle ich mir das so vor, dass man aus dem Abstand eine Skalierungswichtung W für jedes Element ermittelt. Die Gesamtlänge L sowie die Anzahl N der Elemente hast du ja. Dann kann man eine Gleichung aufstellen:
Code:
(W1*x) + (W2*x) + (W3*x) + (...) + (Wn*x) = L bzw.
(W1 + W2 + W3 + ... + Wn) * x = L
Jetzt nach x umstellen und ausrechnen lassen. Dann kannst du für jedes Element die Breite Wi*x errechnen.

Beim zweidimensionalen bin ich mir nicht ganz sicher. Man kann je nach angeklicktem Element (z.B. am Rand des Schachfelds) unterschiedliche Wichtungen in horizontaler und vertikaler Richtung bekommen. Und alle Elemente haben aufeinander Einfluss... :freak:
 
Ja. Soweit war ich schon auch :) . Die Formeln reichen aber nicht aus um die jeweilige Länge zu bestimmen.

Ich habe mir mittlerweile überlegt das so zu machen:

Ich habe eine feste Länge des Panels l,
minimale Länge eines Elementes min,
maximale Länge eines Elementes max,
Anzahl der Elemente n,
aktueller Punkt wo die Maus ist p.

-Länge eines Elements = l / n;
- jetzt ist jedes Element gleich Lang.
- und jetzt kann ich an der Stelle p das Element größer machen bis max.
-das heißt für die Länge anderer Elemente = (l - max) / (n - 1)
- die Elemente daneben definiere ich ein bisschen kleiner (l2).
- so das die Länge übriger Elemente = ((l - max - 2 * l2) / (n -3) ist.
- und das kann ich so lange machen bis die Länge übreger Elemente > min ist.

Das große Problem an diesem "Algorithmus" ist: er berücksichtigt nicht einfach zu viele Ausnahmen.
z.B.:
- was passiert wenn es nur wenige Elemente sind, daher wenn n * max < l ;
- was passiert wenn es sehr viele Elemente sind, daher wenn (n - 1) * min + max > l , daher wenn die Summe einzelner Elemente mit minimaler Länge und einem Element (da wo die Maus ist) mit maximaler Länge schon die gesamt Länge übersteigt.
- was passiert an den Rändern. Nach diesem Algorithmus kann passieren das die Summe aller Längen KLEINER ist als gesamt Länge.
 
ctfwp444 schrieb:
Die Formeln reichen aber nicht aus um die jeweilige Länge zu bestimmen.
Doch, eigentlich schon. Hab es schnell ausprobiert. Reinklicken fügt Elemente hinzu, Mouse-Hover "aktiviert" ein Element.

Dabei sind aber Minimum- und Maximumgrössen sowie die Zoomreichweite nicht berücksichtigt. Das Ganze ist wirklich nicht ganz trivial. Und wie das dann in 2D aussehen soll, kann ich mir auch nicht richtig vorstellen. Irgendwie würden doch immer Lücken entstehen?

Viel Erfolg noch! :)
 
Nein. Das ist genau das was ich brauche. Und 2D wird dann auch nicht mehr schwer sein. Eigentlich absolut das selbe, bloß in die y-Richtung.
Könntest du mir aber verraten wie du das programmiert hast. Ich verstehe es immer noch nicht wenn ich ehrlich bin.
 
Klar, kein Problem. Das ganze habe ich mit Silverlight in C# gemacht. Hier ein Ausschnitt. Wichtig ist die Methode ResizeElements(), der man das aktive Element (Objektreferenz; bei mir Rechteck) übergibt:

Code:
//vorgegebene Gesamtbreite aller Elemente
private const double mLength = 624;

//Grösse aller Elemente anpassen
private void ResizeElements(Rectangle pRect)
{
    //neue Liste der Wichtungen
    List<double> vWeights = new List<double>();

    //Wichtungen für alle Elemente errechnen und in Liste speichern (reelle Werte zwischen 0 und 1; 0 ist am weitesten weg)
    foreach (Rectangle vRect in mStackPanel.Children)
    {
        double vWeight = 1 - ((double)Math.Abs(mStackPanel.Children.IndexOf(pRect) - mStackPanel.Children.IndexOf(vRect))) / mStackPanel.Children.Count;
        vWeights.Add(vWeight);
    }

    //Summe aller Wichtungen
    double vWeightSum = 0;
    foreach (double vWeight in vWeights)
        vWeightSum += vWeight;

    //x als Faktor für die Grössenberechnung jedes Elements (siehe mein Post oben)
    double x = mLength / vWeightSum;

    //jedes Element durchgehen und mit zugehöriger Wichtung und x Grösse festlegen
    for (int i = 0; i < mStackPanel.Children.Count; i++)
        (mStackPanel.Children[i] as Rectangle).Width = vWeights[i] * x;
}

Die Elemente (Rechtecke) werden in einem StackPanel automatisch angeordnet - also selbst positionieren musste ich da nichts. Der Aufwand käme dann wahrscheinlich noch hinzu, wenn man es in 2D macht.
Hier ist noch das Visual Studio (Professional) Projekt - ich weiss nicht, ob du damit was anfangen kannst.
 
Danke schön. Dann versuche ich das zu realisieren. Wenn es klappt stelle ich meine Quellcode hier aus. Wird aber in Java sein (was ja ähnlich ist).
 
Zurück
Oben