setNeedsDisplayInRect: zieht Striche

  • Hier noch der Output bei einer Vergrösserung von 100%, Skalierungsfaktor 1.0:

    Quellcode

    1. 2005-05-02 09:30:59.191 Poster Print[881] -enclosingRectForObjects: {{165.236, 142.793}, {223.789, 132.526}}
    2. 2005-05-02 09:30:59.192 Poster Print[881] -setNeedsDisplayInRect: {{165.236, 142.793}, {223.789, 132.526}}
    3. 2005-05-02 09:30:59.221 Poster Print[881] -setNeedsDisplayInRect: {{165, 142}, {225, 134}}
    4. 2005-05-02 09:30:59.222 Poster Print[881] -drawRect: {{165, 142}, {225, 134}}


    Da zieht es KEINE Striche. Schätze mal das liegt daran, dass das Rect von -drawRect: keine Fliesskommazahlen hat.
  • Daran liegts:

    Quellcode

    1. //- (void)setZoomFactor:(float)factor
    2. [self scaleUnitSquareToSize:NSMakeSize(f, f)];


    Wenn ich diesen Befehl kommentiere, Funktioniert alles. Scheint so als ob der meine Angaben irgendwie umrechnen würde.

    Wie hast du das vorhin gemeint, ich brauche kein UnionRect? Kann ich einfach 20-mal setNeedsDisplayInRect: aufrufen und der errechnet am Schluss das Union Rect für alle rects und zeichnet?
  • Hmmmm, wenn du den Befehl auskommentierst, dann zoomt er doch gar nicht mehr? Oder meintest du den bereits auskommentierten Befehl?

    Ja, das meinte ich übrigens. Ich schrieb ja schon eingangs, dass -setNeedsDisplayInRect:(NSView) lediglich die Region zum zeichnen markiert. Innerhalb einer Event-Loop sammelt er alle invalidated Rects und schickt dann beizeiten ein Redraw-Event, welches in deinem drawRect mündet. Schau dir nochmal die Doku dazu an. Hier nur mal die Methodenbeschreibung von mir hervorgehoben:

    Marks the region of the receiver within invalidRect as needing display, increasing the receiver’s existing invalid region to include it. A later displayIfNeeded... method will then perform drawing only within the invalid region. NSViews marked as needing display are automatically redisplayed on each pass through the application’s event loop. (View objects that need to redisplay before the event loop comes around can of course immediately be sent the appropriate display... method.)
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Cool, dann brauch ich mein -enclosing... gar nicht mehr!
    Ja, ich hab das -scaleByUnitSquareToSize: gemeint. Das Problem ist, dass ich das brauche, damit mein NSTextField richtig angezeigt wird (zum Bearbeiten von Text eines Elements). Mit den Bounds, kann ich da einfach -(NSRect)bounds subclassen und dann die skalierte version zurückgeben (selber skaliert) ?
  • Also, bei mir funktioniert's ...

    Ich glaube auch weiterhin, dass da ein Rundungsfehler vorliegt. Das Fehlerbild ist einfach zu eindeutig.

    Wegen des auf den Kopf stehen: Du hast doch -isFlipped(DeinView) implementiert? Ansonsten musst du setBound und setFrame nicht überschreiben. Benutze sie einfach. Ich mache es auch so.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Klar, bei mir ist das auch ein NSScrollView. Wenn gezoomt werden kann ...

    Hmmmm, hast du dir mal alle Ints aus den beteiligten Modulen heraussuchen lassen?

    Für das Setzen ein einfaches Beispiel, mal schnell aus dem Keller geholt:

    Quellcode

    1. - (void)awakeFromNib {
    2. NSRect bounds;
    3. if ([[self superclass] instancesRespondToSelector:@selector(awakeFromNib)]){
    4. [super awakeFromNib];
    5. }
    6. [self setFrame:[self frame]];
    7. [self setBoundsOrigin:NSMakePoint( -10, -10 ) ];
    8. bounds = [self bounds];
    9. bounds.size.width /= 2;
    10. bounds.size.height /= 2;
    11. [self setBounds:bounds];
    Alles anzeigen

    Das ist jetzt Zoom-Faktor 2. Wichtig ist, dass du die Bounds nach dem Frame setzt, da eine Veränderung des Frames die Bounds mitverändert. Eine setZoom-Methode sieht daher wie folgt aus:

    Quellcode

    1. - (void)setZoom:(float)_zoom {
    2. NSRect frame;
    3. NSRect bounds;
    4. zoom = _zoom;
    5. frame = [self frame];
    6. bounds = [self bounds];
    7. frame.size.width = bounds.size.width * _zoom;
    8. frame.size.height = bounds.size.height * _zoom;
    9. [self setFrameSize: frame.size]; // Change the view's size.
    10. [self setBoundsSize: bounds.size]; // Restore the view's bounds, which causes the view to be scaled.
    11. [self setNeedsDisplay:YES];
    12. }
    Alles anzeigen



    Die Umrechnungsbasis für Maus etc. machst du bitte nicht über Origins! Dafür gibt es -convertPoint:fromView:(NSView) et al.
    Ist aber wie gesagt alter Code, so dass ich keine Gewähr übernehme.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • Nein, zumal die offenbar selbst -setBounds:(NSView) aufrufen.

    Aber bei einer so häufig benutzten Methode, würde es mich doch sehr wundern, wenn sie nicht funktionieren würde. Immerhin dürfte es 98721983791283798 Apps geben, die in einem Scroll-View zoomen. Und bisher habe ich nichts von einem Problem gehört ...

    Aber, das sage ich jetzt einfach mal so, du hast ja schon einige Dinge hingefriemelt (Origin, Union-Rect etwa), du bist auch offenbar noch neu und kanntest viele Funktionen nicht. Hältst du es da nicht mindestens für wahrscheinlich, dass der Fehler bei dir liegt? Was macht dich so sicher, dass es anders ist?
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • hmmm... Stimmt eigentlich schon. Ich begreife einfach nicht, wie der, wenn ich NSMakeRect(120, 150, 200, 300) an setNeedsDisplayInRect: weitergebe, in -drawRect: dann auf (118.32145, 153.4545, 201.54632, 306.55213) kommen kann...
    Es sieht irgendwie so aus, als dass der View, wenn er neu zeichnet, den Bereich aufrunden würde und den löscht, dann aber ein abgerundetes clip-rect erzeugt. Da lieg ich warscheinlich total falsch, aber ich kann mir nicht mehr recht erklären, was da bei mir genau das problem ist.
    Aber ich denke ich spar mir den Bug bis zum Ende auf... :)

    Vielen Dank!
    Gruss,
    Fabian
  • Hmm, also die Vergrößerung ist doch klar: Welche Eigenschaften hat denn dein View? Schatten? Umrahmung? Das rechnet Cocoa automatisch mit ein.

    Du kannst mir ja mal das Projekt schicken. Vielleicht finde ich die Zeit, hereinzuschauen.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?
  • RE: setNeedsDisplayInRect: zieht Striche

    Ich weiss ja nicht, ob sich das Problem mittlerweile erledigt hat, aber hier mein Senf:

    Das sieht in der Tat nach irgend etwas mit Rundung oder Antialiasing aus. Ob das ein wirklicher Rundingsfehler ist, weiß ich nicht. Wie malst Du denn Deinen Hintergrund? Füllst Du das in [drawRect: ] übergebene Rechteck? Setzt Du das Clipping explizit? Das sind die einzigen Möglichkeiten, die ich sehe, dass sowas passieren kann. Denn egal wie Deine rects aussehen: Der scrollView/clipView und Dein documentView bekommen ja das gleiche Clipping, also sollte es unmöglich sein, dass der ClipView irgendwelche Pixel malten kann, die in Deinem [drawRect: ] nicht erreicht werden. Wenn das so wäre, würde ich an diesen Stellen das Rechteck etwas ausweiten.

    Etwas anderes: Deine floor()- und ceil()-Geschichte tut wahrscheinlich nicht immer das, was Du bezweckst. Ein Beispiel: Ein Rect mit dem Ursprung (3.9,3.9) und Größe (3.9,3.9) hat logischerweise als rechte untere Ecke (7.8,7.8 ). Nach Deiner Rundung wird's zu Origin (3.0,3.0) und Size (4.0,4.0). Also liegt die untere Ecke nun bei (7.0,7.0) - der Bereich zwischen 7.0 und 7.8 fehlt! Schau mal nach NSIntegralRect() - das sollte es tun.

    Das aber nur nebenbei - ich habe ein ähnliches Programm hier, mit View in ScrollView und [setNeedsDisplayInRect: ], das macht keine Blitzer, auch ganz ohne Rundung.
    Multigrad - 360°-Produktfotografie für den Mac
  • Hi,

    Das in drawRect: angegebene Rect fülle ich nicht von Hand, ich hab lediglich dem ScrollView eine Hintergrundfarbe gegeben. Das Clipping habe ich bis jetzt nicht mal annähernd berührt.
    Das eigen floor-ceil Zuegs habe ich schon rausgenommen und grösstenteils durch NSIntegralRect ersetzt.
    Was ist denn genau ein Rundungsfehler? Also wie sieht der aus? Was der tut merk ich jagerade... ;)

    Gruss,
    Fabian
  • Nun, durch das Runden von Zahlen verlierst Du ja Information. Im Falle von Grafikkoordinaten verschieben sich dadurch Punkte. Wie sich das auswirken kann, hat mattik in seinem Beispiel eigentlich sehr schön beschrieben. In dem Beispiel ist das Rechteck durch Runden zwar Größer geworden, aber es hat sich auch so verschoben, dass es den ursprünglichen Bereich nicht mehr vollständig überdeckt.

    Michael
  • Na, sowohl mattik als auch ich haben dir doch Beispiele genannt.

    Das Problem ist, dass du möglicherweise weniger "draufrundest" als du durch die Umwandlung in Ints verlierst. Richtig wäre es, wenn du die Punktkoordinaten runden würdest anstelle Origin/Size. Also, nochmal mit den Zahlen von mattik (meine sind auf der ersten Siete, kannst ja noch einmal reinscheuen.

    Origin = 3,9|3,9
    Size = 3,9|3,9

    Nehmen wir nur die X-Richtung:
    Das ist der Anfangspunkt3,9 und der Endpunkt 7,8. Wenn du Origin und weite rundest, kommst du auf 3 und 4, was im Ergebenis die Punkt 3 und 7 bringt. Die Strecke 7,0 bis 7,8 wird nicht gezeichnet. Abhilfe schafft hier das Runden der jeweiligen Punkt: 3 und 8 ergeben sich dann, die du als 3 und Weite 5 speicherst.

    Aber das Ganze solltest du ohnehin weglassen. Koordinaten _sind_ floats. Die frühere Annahme, es seien Flächen, ist unter Cocoa fehlerhat. Das ist auch nicht nur eine Zahlenspielerei, sondern hat Konsequenzen. Hier habe ich das etwa an einem Beispiel beschrieben:
    Bild zeichnen. Mal scharf, mal unscharf.

    Also, langer Rede kurzer Sinn: Runde nicht!

    Ob du das Superview zeichnest schaue ich bei Gelegenheit mal nach. Es kann durchaus sein, dass das drawRect einen _größeren_ Bereich als das eigentlich View fordert. Dann muss das Superview freilich den Hintergrund zeichnen.
    Es hat noch nie etwas gefunzt. To tear down the Wall would be a Werror!
    25.06.2016: [Swift] gehört zu meinen *Favorite Tags* auf SO. In welcher Bedeutung von "favorite"?