NSTextContainer und NSBezierPath

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • RE: NSTextContainer und NSBezierPath

    Es gibt folgende Fälle (neben den einfachsten)
    Zur Vereinfachung der Diskussion sagen wir erst einmal, dass ein Weg ein Pfad ist, der geschlossen ist und abgehbar ist. Also etwa ein Kreis, eine Schlaufe usw. Ein Pfad kann aus mehreren Wegen bestehen, etwa zwei verschachtelte Kreise

    - Dann kannst du einen Kreis im kreis haben. Der innere darf nicht gezeichnet werden. Dazu muss ich aber doch nicht extra tesselieren? Ich schneide das erwartete Rechteck einfach an allen Pfaden. Bei der hier angewendeten Even-Odd-Windong-Rule, ist _jeder_ Schnittpunkt mit einem Pfad eine Grenze des Rechteckes.

    - Du hast Schlaufen. Hier gilt das gleiche. Einfach mit jedem Pfad schneiden. Klappt doch!?

    Davon unabhängig kanst du bei Beziers natürlich mehrere Ergebnis-Rechtecke haben. Stell dir nur ein Quadrat vor, welches oben einen Halbkreis als Beule mach innen hat. Das ist aber ein ganz anderes Problem. und hierfür gibt es ja remaining Rect.

    Also, tesselieren muss man da nicht.
    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: NSTextContainer und NSBezierPath

    Also, ich habe noch einmal nachgedacht. Du musst nicht tesselieren. Du findest einfach die Schnittpunkte zu jedem einzelnen Fragment der Bezierpaths. Am Anfang musst du natürlich einen inside-Test machen, was aber kein Problem ist. Aufpassen musst du auch, dass du Berührungspunkte nicht als Schnittpunkte nimmst. Ich meine, das müsste problemlos funktionieren.
    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: NSTextContainer und NSBezierPath

    Passt nicht ganz zum Thema, aber ich packe es trotzdem mal hier hin.

    Ich habe mir einen NSTextStorage, einen NSLayoutManager und einen NSTextController angelegt, welche, entsprechend der Docu, miteinander verknüpft sind. In das NSTextStorage packe ich meinen Text als NSAttributedString. Dann setze ich im NSTextStorage noch das Attribute NSParagraphStyleAttributeName für den kompletten Text. Dieses NSParagraphStyle hat NSLineBreakByClipping und NSLeftTextAlignment oder NSRightTextAlignment gesetzt. Danach lege ich im NSTextContainer über setContainerSize eine max. Größe fest.

    Wenn ich jetzt im NSLayoutManager boundingRectForGlyphRange: inTextContainer: aufrufe, dann bekomme ich immer einen NSRect.origin.x von 5 und eine NSRect.size.width welche 10 kleiner als die oben gesetzte max. Größe ist. Warum ist dies so und wo muss ich drehen, damit ich einen Offset von 0 erhalte?

    Wenn ich vorher im NSParagraphStyle NSRightTextAlignment gesetzt habe und ich gebe den Text dann über den NSLayoutManager mit drawBackgroundForGlyphRange: atPoint: und drawGlyphsForGlyphRange: atPoint: aus, wird der Text immer am Ende (also rechts) abgeschnitten, obwohl ich bei NSRightTextAlignment erwartet hätte, dass der Text am Anfang (also links) abgeschnitten wird. Ich könnte zwar im NSParagraphStyle NSLineBreakByTruncatingHead setzen, doch dann erhalte ich links ... wenn der Text zu lang ist. Wie kann ich den Text links ohne ... abschneiden, damit dieser bis zum Ende rechtsbündig angezeigt wird?
  • RE: NSTextContainer und NSBezierPath

    Original von MCDan
    Wenn ich jetzt im NSLayoutManager boundingRectForGlyphRange: inTextContainer: aufrufe, dann bekomme ich immer einen NSRect.origin.x von 5 und eine NSRect.size.width welche 10 kleiner als die oben gesetzte max. Größe ist. Warum ist dies so und wo muss ich drehen, damit ich einen Offset von 0 erhalte?

    NSTextContainer hat noch eine Eigenschaft "lineFragmentPadding". Das ist der Schuldige.

    Quellcode

    1. [textContainer setLineFragmentPadding:0.0];
    Damit sollte das Problem gelöst sein.

    Original von MCDan
    Wenn ich vorher im NSParagraphStyle NSRightTextAlignment gesetzt habe und ich gebe den Text dann über den NSLayoutManager mit drawBackgroundForGlyphRange: atPoint: und drawGlyphsForGlyphRange: atPoint: aus, wird der Text immer am Ende (also rechts) abgeschnitten, obwohl ich bei NSRightTextAlignment erwartet hätte, dass der Text am Anfang (also links) abgeschnitten wird.

    Ich bin mir nicht sicher, ob ich dieses Problem richtig verstanden habe. Geht es hier um Text, der von rechts nach links gelesen wird? NSRightTextAlignment sagt ja nur, das der Text am rechten Rand ausgerichtet wird. Über die Leserichtung sagt dieser Parameter gar nichts aus. Das ist Sache der "Writing Direction"

    Michael
  • RE: NSTextContainer und NSBezierPath

    Original von Michael

    NSTextContainer hat noch eine Eigenschaft "lineFragmentPadding". Das ist der Schuldige.

    Quellcode

    1. [textContainer setLineFragmentPadding:0.0];

    Jepp, jetzt ist NSRect.origin.x = 0 bzw. hat den Wert, den ich mit setLineFragmentPadding: gesetzt habe. Danke!

    Original von Michael

    Ich bin mir nicht sicher, ob ich dieses Problem richtig verstanden habe. Geht es hier um Text, der von rechts nach links gelesen wird? NSRightTextAlignment sagt ja nur, das der Text am rechten Rand ausgerichtet wird. Über die Leserichtung sagt dieser Parameter gar nichts aus. Das ist Sache der "Writing Direction"

    Der Text soll schon von links nach rechts laufen, nur soll er halt rechtsbündig sein. Wenn der Text also von der Länge nicht passt, dann soll dieser bis zum Ende angezeigt werden und alles was dann vom Anfang nicht passt abgeschnitten werden. Allerdings sollen nicht mit der Option NSLineBreakByTruncatingHead diese Punkte (...) angezeigt werden.

    Das Problem scheint die Berechnung des drawRange mit:

    Quellcode

    1. NSRange drawnRange = [layoutManager glyphRangeForBoundingRect:NSMakeRect(0,0,size.width,size.height)
    2. textContainer:textContainer];
    zu sein, da mir hier immer eine NSRange.location von 0 zurückgegeben wird. Der Range scheint also immer linksbündig berechnet zu werden. Wie kann ich den Range rechtsbündig berechnen lassen, so dass ich eine NSRange.location von > 0 (mit einer NSRange.length bis zum Ende) erhalte?
  • RE: NSTextContainer und NSBezierPath

    Original von MCDan
    Wenn der Text also von der Länge nicht passt, dann soll dieser bis zum Ende angezeigt werden und alles was dann vom Anfang nicht passt abgeschnitten werden. Allerdings sollen nicht mit der Option NSLineBreakByTruncatingHead diese Punkte (...) angezeigt werden.

    Ach so. Dann sollte die Option NSLineBreakByClipping das Richtige sein.

    Original von MCDan
    Das Problem scheint die Berechnung des drawRange mit:

    Quellcode

    1. NSRange drawnRange = [layoutManager glyphRangeForBoundingRect:NSMakeRect(0,0,size.width,size.height)
    2. textContainer:textContainer];
    zu sein, da mir hier immer eine NSRange.location von 0 zurückgegeben wird. Der Range scheint also immer linksbündig berechnet zu werden. Wie kann ich den Range rechtsbündig berechnen lassen, so dass ich eine NSRange.location von > 0 (mit einer NSRange.length bis zum Ende) erhalte?

    Die Range, die Du hier zurück bekommst, gibt den Bereich von Glyphs an, der in dem TextContainer rein passt. Wo diese Glyphs in dem TextContainer angeordnet sind, ist für die Range belanglos. Das ist vergleichbar mit der Range in einem String. Die Glyph-Range ist auch direkt mit einer Range im zugehörigen String verknüpft. Wenn das erste Zeichen in dem TextContainer im TextStorage der Character mit dem Index 0 ist, dann ist auch immer die Glyph-Range.location = 0.
    Mal ein ganz einfaches Beispiel: Ein String mit nur einem Zeichen wird in einem TextContainer dargestellt. Nehmen wir mal den String "A". In dem Fall wäre die Range des Strings {0, 1} und die Gyph-Range ebenfalls {0, 1}.
    Nun stellt sich mancher sicherlich die Frage, was ist nun der Unterschied zwischen diesen beiden Ranges? Nehmen wir nun mal einen anderen String, z.B. "é". Die Range des Strings wäre immer noch {0, 1} aber die Glyph-Range wäre {0, 2}, weil das Zeichen "é" aus zwei verschiedenen Glyphs zusammengesetzt wird, nämlich aus "e" und "´".

    Michael
  • RE: NSTextContainer und NSBezierPath

    Original von Michael

    Die Range, die Du hier zurück bekommst, gibt den Bereich von Glyphs an, der in dem TextContainer rein passt. Wo diese Glyphs in dem TextContainer angeordnet sind, ist für die Range belanglos. Das ist vergleichbar mit der Range in einem String. Die Glyph-Range ist auch direkt mit einer Range im zugehörigen String verknüpft. Wenn das erste Zeichen in dem TextContainer im TextStorage der Character mit dem Index 0 ist, dann ist auch immer die Glyph-Range.location = 0.
    Mal ein ganz einfaches Beispiel: Ein String mit nur einem Zeichen wird in einem TextContainer dargestellt. Nehmen wir mal den String "A". In dem Fall wäre die Range des Strings {0, 1} und die Gyph-Range ebenfalls {0, 1}.
    Nun stellt sich mancher sicherlich die Frage, was ist nun der Unterschied zwischen diesen beiden Ranges? Nehmen wir nun mal einen anderen String, z.B. "é". Die Range des Strings wäre immer noch {0, 1} aber die Glyph-Range wäre {0, 2}, weil das Zeichen "é" aus zwei verschiedenen Glyphs zusammengesetzt wird, nämlich aus "e" und "´".

    D.h. die Glyph-Range ist, bis auf einige Ausnahmen, immer so gross wie die Text-Range auch wenn der komplette Text gar nicht passen würde.

    Warum wird aber mein Text immer von links beginnend in der max. Länge ausgegeben, obwohl ich im NSParagraphStyle setAlignment:NSRightTextAlignment gesetzt habe?
  • RE: NSTextContainer und NSBezierPath

    Original von MCDan
    D.h. die Glyph-Range ist, bis auf einige Ausnahmen, immer so gross wie die Text-Range auch wenn der komplette Text gar nicht passen würde.

    Wenn der Text in einen Container passt, dann sind die beiden Ranges sehr ähnlich. Hängt halt davon ab, was für Zeichen so vorkommen. Erstreckt sich der Text aber über mehrere TextContainer, dann bekommst Du als Glyph-Range nur die Range des jeweiligen TextContainers zurück. Dann ist Range.location ab dem zweiten TextContainer >0. Die Range des gesamten Strings ist dann größer als die Glyph-Range eines TextContainers.

    Original von MCDan
    Warum wird aber mein Text immer von links beginnend in der max. Länge ausgegeben, obwohl ich im NSParagraphStyle setAlignment:NSRightTextAlignment gesetzt habe?

    Weil die Ausrichtung des Textes nicht mit der Anzahl der in einem TextContainer verarbeiteten Zeichen/Glyphs verknüpft ist.

    Ich glaube ich mache nachher mal eine kleines Demoprojekt.

    Michael
  • RE: NSTextContainer und NSBezierPath

    Original von Tom9811
    Also, ich habe noch einmal nachgedacht. Du musst nicht tesselieren. Du findest einfach die Schnittpunkte zu jedem einzelnen Fragment der Bezierpaths. Am Anfang musst du natürlich einen inside-Test machen, was aber kein Problem ist. Aufpassen musst du auch, dass du Berührungspunkte nicht als Schnittpunkte nimmst. Ich meine, das müsste problemlos funktionieren.

    Ja, das kommt einer Lösung schon ziemlich nahe. Nur, wie Du sagst, funktioniert das nur mit even-odd - bei nonzero müsste man zwischen relevanten und irrelevanten Pfadabschnitten unterscheiden können. Ein anderes Problem: Löcher, die komplett im proposedRect liegen. Das ließe sich sicherlich mittels eines bounding-box-Tests ausschließen. Bei Sattelpunkten auf dem Boxrand etc. könnte die Unterscheidung zwischen Schnitt- und Berührungspunkten nochmal etwas frickelig werden, aber das ist sicherlich machbar. Die Lösung würde vermutlich Lücken bei aufeinander verlaufenden Pfadabschnitten erzeugen (z.B. bei einem Loch und einem exakt darauf passenden Deckel), aber erstens ist dieser Fall recht konstruiert und zweitens wäre dann ohnehin nicht eindeutig, welches Verhalten gewünscht (geschweige denn richtig) ist. Weitere Ausnahmen fallen mir momentan auch keine mehr ein - wenn es welche gibt, fallen sie spätestens beim Implementieren auf...

    Tom, ich muss zugeben, dass Du da wieder eine Wunde aufgerissen hast - ich hatte die direkte algebraische Lösung angedacht, aber relativ schnell aufgrund von Bedenken bezüglich Ausnahmen, numerischer Instabilität etc. wieder verworfen. Jetzt werde ich den Ansatz wohl wieder in die engere Wahl ziehen und etwas damit herumbasteln. Damit lege ich mich auf even-odd fest, aber das ist verschmerzbar.

    Danke für Eure Anregungen!
    Multigrad - 360°-Produktfotografie für den Mac
  • RE: NSTextContainer und NSBezierPath

    Original von MCDan
    Wäre echt super! Vielleicht einmal mit links und rechts ausgerichtetem Text, der zu lang ist und entsprechend abgeschnitten werden muss. =)

    So, ich habe da jetzt mal was zusammengehackt, mit dem man ein wenig herumspielen kann. Nun ist mir Dein Problem auch klar geworden und ich denke, man könnte das unter Umständen als einen Bug in Cocoa bezeichnen. Eine elegante Lösung fällt mir nämlich nicht ein. Ich hoffe, das kleine Demoprogramm macht trotzdem einiges klarer.

    Michael
  • RE: NSTextContainer und NSBezierPath

    Original von Michael
    So, ich habe da jetzt mal was zusammengehackt, mit dem man ein wenig herumspielen kann. Nun ist mir Dein Problem auch klar geworden und ich denke, man könnte das unter Umständen als einen Bug in Cocoa bezeichnen. Eine elegante Lösung fällt mir nämlich nicht ein. Ich hoffe, das kleine Demoprogramm macht trotzdem einiges klarer.

    Danke, das Demoprogramm ist super und lässt genau mein Problem erkennen. Wenn bei den Einstellungen NSLineBreakByClipping + NSRightTextAlignment ein Text zu lang für eine Zeile wird, dann wird dieser Text nicht mehr rechtsbündig sondern linksbündig ausgegeben. Genau dieses Verhalten habe ich nicht verstanden und daher auf einen Bedienungs-/Einstellungsfehler am Text System meinerseits getippt.

    Wenn's Dir nichts ausmacht, dann nehme ich das Demoprogramm mit zur WWDC und werde mal die Entwickler bei Apple fragen, ob dieses Feature so gewünscht ist. ;)
  • RE: NSTextContainer und NSBezierPath

    Original von MCDan
    Danke, das Demoprogramm ist super und lässt genau mein Problem erkennen. Wenn bei den Einstellungen NSLineBreakByClipping + NSRightTextAlignment ein Text zu lang für eine Zeile wird, dann wird dieser Text nicht mehr rechtsbündig sondern linksbündig ausgegeben.

    Na ja, rein optisch betrachtet ist das schon auch linksbündig. Es wir lediglich der falsche "Ausschnitt" der zu langen Zeilen gesetzt.

    Original von MCDan
    Wenn's Dir nichts ausmacht, dann nehme ich das Demoprogramm mit zur WWDC und werde mal die Entwickler bei Apple fragen, ob dieses Feature so gewünscht ist. ;)

    Ja, mach das. Habe nichts dagegen.

    Michael
  • RE: NSTextContainer und NSBezierPath

    Original von MCDan
    Danke, das Demoprogramm ist super und lässt genau mein Problem erkennen. Wenn bei den Einstellungen NSLineBreakByClipping + NSRightTextAlignment ein Text zu lang für eine Zeile wird, dann wird dieser Text nicht mehr rechtsbündig sondern linksbündig ausgegeben. Genau dieses Verhalten habe ich nicht verstanden und daher auf einen Bedienungs-/Einstellungsfehler am Text System meinerseits getippt.


    Das Cocoa text System ist zwar hammergeil, aber es hat auch mindestens genau so viele Bugs. Und einige davon sind nicht marginal. Wenn beim Tippen einfach Text verschwindet, dass muss doch was faul sein, oder?? ;)
  • RE: NSTextContainer und NSBezierPath

    Original von Michael
    So, ich habe da jetzt mal was zusammengehackt, mit dem man ein wenig herumspielen kann. Nun ist mir Dein Problem auch klar geworden und ich denke, man könnte das unter Umständen als einen Bug in Cocoa bezeichnen. Eine elegante Lösung fällt mir nämlich nicht ein.

    Schöne Demo! Der Fehler ist zwar ärgerlich, und sicherlich nicht das, was man erwartet, aber, wie ich finde, nicht wirklich ein Bug. Das gewünschte Verhalten ist halt nicht eindeutig. Statt einer Verwurstung von Alignment und Line breaks wären mir neue Konstanten wie NSLineBreakByClippingRight, NSLineBreakByClippingLeft, NSLineBreakByClippingMiddle lieber - oder noch besser die Möglichkeit, die Ellipse ("...") bei den Truncate-Modi zu ändern.

    Ein ClipView um den TextView ist sicherlich nicht elegant, sollte aber das Problem lösen können, oder?
    Multigrad - 360°-Produktfotografie für den Mac
  • RE: NSTextContainer und NSBezierPath

    Original von mattik
    Schöne Demo! Der Fehler ist zwar ärgerlich, und sicherlich nicht das, was man erwartet, aber, wie ich finde, nicht wirklich ein Bug.

    Deshalb schrieb ich auch "unter Umständen". Man weiß ja nicht, ob es so gewollt ist oder nicht. Aber in ein paar Tagen kriegen wir ja hoffentlich eine Antwort aus erster Hand.

    Original von mattik
    Ein ClipView um den TextView ist sicherlich nicht elegant, sollte aber das Problem lösen können, oder?

    Das wäre die einzige Lösung, dir mir auch eingefallen ist. Allerdings hat man dabei das Problem, dass man bei -drawGlyphsForGlyphRange:atPoint: jedesmal den "Point" anpassen muss, wenn sich die Breite des Textes ändert.

    Michael
  • RE: NSTextContainer und NSBezierPath

    @mattik(?)

    Naja, eine ganz algabraische Lösung würde ich nicht nehmen. Zu den Berührungs- vs. Schnittpunkten. Das löst sich recht einfach algorithmisch:

    1. Du suchst den nächsten/vorhergehenden Berührungpunkt oder Schnittpunkt (nennen wir den Oberbegriff für Schnitt- und Berührungspunkte Kandidaten). Dann tast du ein t mit dem zu testendenPunkt ein tv für den vorangegangenen und ein tn für den folgenden. Hast du keinen weiteren Kandidaten, weil der Pfad im proposedRect endet (oder beginnt) nimmst du ersatzweise einfach den Punkt. Der ist sozusagen ein "implizieter Kandidat".

    2. Du machst zwei neue t, nämlich tn' und tv' die jeweils der Mittelwert zwischen t und tn bzw. tv sind. Die testest du auf insideRect. Ist das Ergebnis gleich, so handelt es sich um eine reine Berührung. Ist das Ergebnis verschieden, so handelt es sich um einen echten Schnittpunkt inkl. Sattelpunkt. Da Bezierpfade stetig sind, muss es einen Punkte zwischen den Kandidaten geben -- bis zur Auflösung von float hin. Wenn du darunter landest, hast du mutmaßlich aber ganz andere Probleme (und nebenbei der Betrachter mit seiner 12379230172918321982373219fach vergrößernden Lupe).

    Da du ständig diese Kandidaten-Tests benötigst, empfiehlt es sich wohl, bei der ersten Übergabe des proposedRects einmal alle Kandidaten zu merken. Das wäre eine sinnvole Optimierung.

    Ich muss jetzt unterrichten. Wenn ich wiederkomme, mach ich mal eine Pseudo-Code, damit klarer wird, was ich meine.

    Quellcode

    1. //assume we have this
    2. NSArray* candidates = ...
    3. // Mark all candadates as touching, leaving or entering
    4. // We scan all candidates I do a enumeration, probably traversing by index would be better
    5. // Another solution seems to be to do a prev -> actual -> next queue.
    6. // Anyway, this is sample code for understanding
    7. Candidate* previousCandidate;
    8. Candidate* nextCandidate;
    9. Candidate* candidate;
    10. NSEnumerator* candidatesEnum = [candidates objectEnumerator];
    11. while( (candidate = [candidatesEnum nextObject]) )
    12. // if it is the first candidate, we do not have a previous one. We take the start point for this otherwise the previous one.
    13. if( candidate == [candidates firstObject] )
    14. previousCandidate = [Candidate candidateAtT:0.0];
    15. else
    16. previousCandidate = [candidates objectAtIndex:[candidates indexOfObject:candidate]-1];
    17. // if it is the last candidate, we do not have a next one. We take the end point for this otherwise the next one
    18. if( candidate == [candidates lastObject] )
    19. previousCandidate = [Candidate candidateAtT:1.0];
    20. else
    21. previousCandidate = [candidates objectAtIndex:[candidates indexOfObject:candidate]+1];
    22. // Now we have three candis. We do not eat them (I would like to), but check beetween them
    23. comesFromInside = NSPointInRect( [path pointForT:([candidate t] - [previousCandidate t])/2, myRect );
    24. // goes to inside
    25. goesToInside = NSPointInRect( [path pointForT:([nextCandidate t] - [candidate t])/2, myRect );
    26. // come equals go, we have only a touch
    27. if( comesFromInside == goesToInside ) {
    28. // very interesting -- but not to me
    29. [candidate setCrossingMode:CandidateTouching];
    30. } else {
    31. if( comesFromInside ) {
    32. // we have a cross over, which _leaves_ the rect -> the rest of the rect is _valid_
    33. [candidate setCrossingMode:CandidateLeaving];
    34. } else {
    35. // we have a cross over, which _enters_ the rect -> the rest is _invalid_ for drawing
    36. [candidate setCrossingMode:Entering];
    37. } // comes from
    38. } // is touching
    39. } // all candidates
    Alles anzeigen


    Natürlich wie immer ungetstet und daher ohnehin nur: So oder so ähnlich
    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: NSTextContainer und NSBezierPath

    Original von Tom9811
    Naja, eine ganz algabraische Lösung würde ich nicht nehmen. Zu den Berührungs- vs. Schnittpunkten. Das löst sich recht einfach algorithmisch:
    ...

    Klingt einleuchtend. So in etwas hatte ich's mir auch gedacht und hätte es auch so gemacht, wenn es notwendig gewesen wäre - war es im Endeffekt aber doch nicht. Mittlerweile läuft's ziemlich rund (und sogar schnell). Nur falls es irgend jemanden interessiert, so habe ich's gemacht:

    Die Zerteilung findet nicht für jedes fragmentRect einzeln statt, sondern gleichzeitig für alle fragmentRects in einer Zeile. Deshalb, weil ohnehin alle rects bei dem Test anfallen und deshalb, weil der Typesetter bei mehreren Fragments logischer- und üblicherweise alle rects nacheinander abfragt. Die fragmentRects werden in einem Cache gespeichert, der bei jeder Änderung der Zeile oder der zugrunde liegenden Geometrie invalidiert wird. Bei jeder Anfrage wird der Cache validiert und anschließend das entsprechende Fragment aus der Liste herausgesucht.

    Und so läuft die Aktualisierung des Caches (der einfacheren Begrifflichkeit halber für horizontalen Sweep, für den vertikalen einfach x und y vertauschen):

    Für die weitere Berechnung sind die obere und die untere Grenze konstant. Sie bilden einen Streifen. Eine leere Liste für schmutzige Intervalle entlang der x-Achse erstellen ("schmutzige" Intervalle nenne ich diejenigen, innerhalb derer ein Pfadabschnitt durch den Streifen läuft.

    Nun jeden Patch des Pfades durchgehen (Das frühe Zerteilen des Pfades in Patches erleichtert die Arbeit enorm!):

    - Test, ob der Patch offensichtlich komplett ausserhalb des Streifens liegt (also alle Kontrollpunkte entweder ober- oder unterhalb). Diese können übersprungen werden.

    - Für jeden Patch die kubischen Funktionen px(t) und py(t) basteln (die beiden Funktionen bilden für t=[0..1] die Koordinaten aller Punkte auf dem Spline-Patch). py(t) mit der oberen und unteren Grenze schneiden. Dabei ist es egal, ob es Berührungs- oder Schnittpunkte sind. Geraden auf einer Grenze (also mit unendlich Schnittpunkten) können komplett ignoriert werden. Schnittpunkte in einer Liste sammeln, doppelte und diejenigen außerhalb des Intervalls ]0..1[ rausschmeißen, die Grenzen 0 und 1 hinzufügen, aufsteigend sortieren. Jetzt entsprechen die Intervalle zwischen benachbarten Einträgen den Abschnitten des Patches, die entweder komplett innerhalb oder außerhalb des Streifens liegen. Für jedes Intervall:

    -- Testen: Sie liegen innerhalb, wenn py(t) mit t=(tMin+tMax)/2 innerhalb des Streifens liegt. Intervalle außerhalb verwerfen. Für die verbleibenden errechnen, in welchen x-Intervall sie liegen (kubische Funktionen sind stetig, deshalb wird aus einem t-Intervall genau ein x-Intervall). Dazu tMin und tMax in px(t) einsetzen und berechnen. Beide Ergebnisse merken. Zusätzliche Kandidaten sind alle lokalen Extrema von px(t), die innerhalb des Intervalls liegen (die Nullstellen von px'(t)). Auch in px(t) einsetzen, auch merken. Nun das Minimum und Maximunm aller gemerkten Werte bilden -> x-Intervall. Dieses Intervall in die anfangs gemachte Liste eintragen, dabei überlappende und aneinandergrenzende Intervalle zusammenfassen (das ist der Grund, warum die Frage "Berührungs- oder Schnittpunkt" oben irrelevant war, schlimmstenfalls haben wir hier ein Intervall mehr bearbeitet, jetzt wandern sie ohnehin wieder zusammen).

    Nach dieser Prozedur sollte in der Liste der schmutzigen Intervalle jeder Bereich des Streifens eingetragen sein, durch den ein Pfad läuft. Nun den Bereich invertieren, also die Liste der "sauberen" Intervalle, durch die kein Pfad läuft. Diese liegen entweder komplett innerhalb oder komplett außerhalb des Pfades. Also die Liste filtern: Nur diejenigen, die innerhalb liegen, bleiben. Das geht wieder mit einem Punkttest in der Mitte des Rechtecks, das horizontal durch die Intervallgrenzen und vertikal durch die Grenzen des Streifens gebildet wird. Fertig!

    Ach ja: Tom, ich hoffe, dass Du nichts über Spline-Analyse unterrichtet hast - nicht, weil ich Dir das nicht zutraute, sondern weil ich glaube, dass zumindest meine Studis bei sowas längst auf Durchzug gestellt hätten, obwohl ihnen das vermutlich besser liegen müßte - traurig aber wahr :)
    Multigrad - 360°-Produktfotografie für den Mac
  • RE: NSTextContainer und NSBezierPath

    *lach* Nein, ich weiß zwar nicht mehr, was es war, aber kann nur Wertpapierrrecht oder IT-/Medienrecht gewesen sein.

    Aber das ist hier ja auch mehr heruntergeschrieben, zumal ich bei dir zurecht davon ausging, dass lange Erläuterungen überflüssig sind.

    BTW: Mit der Irrelevanz beschäftige ihc mich noch einmal. Mir ging es eigentlich mehr darum, dass man sagen kann, dass bei jedem Schnittpunkt ein Wechsel von außen nach innen stattfindet. Aber in der Tat muss man das wohl gar nicht machen.
    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: NSTextContainer und NSBezierPath

    Original von Michael
    Original von MCDan
    Danke, das Demoprogramm ist super und lässt genau mein Problem erkennen. Wenn bei den Einstellungen NSLineBreakByClipping + NSRightTextAlignment ein Text zu lang für eine Zeile wird, dann wird dieser Text nicht mehr rechtsbündig sondern linksbündig ausgegeben.

    Na ja, rein optisch betrachtet ist das schon auch linksbündig. Es wir lediglich der falsche "Ausschnitt" der zu langen Zeilen gesetzt.

    Original von MCDan
    Wenn's Dir nichts ausmacht, dann nehme ich das Demoprogramm mit zur WWDC und werde mal die Entwickler bei Apple fragen, ob dieses Feature so gewünscht ist. ;)

    Ja, mach das. Habe nichts dagegen.

    Nach dem ich 3 Tage erfolglos einen Apple Cocoa Developer gesucht habe, bin ich dann am Freitag auf dem Cocoa and Core Data Feedback Forum fündig geworden. Leider hatten die beiden Entwickler dann nicht mehr genug Zeit, um sich das Problem mit dem Demoprogramm einmal genauer anzuschauen und so soll ich jetzt einen Bug Report mit Beispiel schicken. :(

    Ich würde Dein Programm jetzt ein wenig verändern (Text auf English) und zusammen mit einem Bug Report an Apple schicken.

    Na ja, für die nächste WWDC kenne ich jetzt wenigstens ein paar Apple Entwickler und deren Spezialgebiete. ;)