.Netto-Gegenteil von GraphicsPath.Erweitern()
Ich brauche das Gegenteil von der GraphicsPath.Widen()
Methode .Net:
public GraphicsPath Widen()
Den Widen()
Methode nicht akzeptieren, ein negativer parameter, also ich brauche das entspricht einer Inset
Methode:
public GraphicsPath Inset()
Dazu können Sie in der open-source-Inkscape (www.Inkscape.org), indem Sie auf Menü und wählen Sie "Pfad /Inset" (der eingesetzte Betrag ist gespeichert in der Inkscape-Eigenschaften-dialog). Da Inkscape ist open source, es sollte möglich sein, dies zu tun C#.Net aber ich kann nicht Folgen die Inkscape-C++ - Quelldatei, die für das Leben von mir (und ich brauche nur diese eine Funktion, so kann ich es nicht rechtfertigen, Lern C++, um diese abzuschließen).
Im Grunde, ich brauche ein GraphicsPath-Erweiterung-Methode mit der folgenden Signatur:
public static GraphicsPath Inset(this GraphicsPath original, float amount)
{
//implementation
}
Wie die Signatur-Staaten, es wird ein GraphicsPath
Objekt und .Inset()
den Weg durch eine übergebene Menge... genauso wie Inkscape heute tut. Wenn es vereinfacht die ganze Angelegenheit jeder, der GraphicsPaths in Frage, die sind alle aus der .PolyBezier
Methode (und sonst nichts), so gibt es keine Notwendigkeit zu berücksichtigen, für rects, Ellipsen oder anderen Formen, es sei denn, Sie wollen es der Vollständigkeit halber.
Leider habe ich keine Erfahrung mit C++ - code, so dass seine fast unmöglich für mich zu Folgen die C++ - Logik enthalten in Inkscape.
.
[EDIT:]
Wie gewünscht, hier der "MakeOffset" Inkscape-code. Der zweite parameter (Doppel-Dez) wird negativ sein für ein Einschub, und der absolute Wert dieses Parameters ist die Menge zu bringen, in die Form.
Ich weiß, dass es eine Menge von Abhängigkeiten hier. Wenn Sie brauchen, um mehr zu sehen von der Inkscape-Quellcode-Dateien, Sie sind hier: http://sourceforge.net/projects/inkscape/files/inkscape/0.48/
int
Shape::MakeOffset (Shape * a, double dec, JoinType join, double miter, bool do_profile, double cx, double cy, double radius, Geom::Matrix *i2doc)
{
Reset (0, 0);
MakeBackData(a->_has_back_data);
bool done_something = false;
if (dec == 0)
{
_pts = a->_pts;
if (numberOfPoints() > maxPt)
{
maxPt = numberOfPoints();
if (_has_points_data) {
pData.resize(maxPt);
_point_data_initialised = false;
_bbox_up_to_date = false;
}
}
_aretes = a->_aretes;
if (numberOfEdges() > maxAr)
{
maxAr = numberOfEdges();
if (_has_edges_data)
eData.resize(maxAr);
if (_has_sweep_src_data)
swsData.resize(maxAr);
if (_has_sweep_dest_data)
swdData.resize(maxAr);
if (_has_raster_data)
swrData.resize(maxAr);
if (_has_back_data)
ebData.resize(maxAr);
}
return 0;
}
if (a->numberOfPoints() <= 1 || a->numberOfEdges() <= 1 || a->type != shape_polygon)
return shape_input_err;
a->SortEdges ();
a->MakeSweepDestData (true);
a->MakeSweepSrcData (true);
for (int i = 0; i < a->numberOfEdges(); i++)
{
// int stP=a->swsData[i].stPt/*,enP=a->swsData[i].enPt*/;
int stB = -1, enB = -1;
if (dec > 0)
{
stB = a->CycleNextAt (a->getEdge(i).st, i);
enB = a->CyclePrevAt (a->getEdge(i).en, i);
}
else
{
stB = a->CyclePrevAt (a->getEdge(i).st, i);
enB = a->CycleNextAt (a->getEdge(i).en, i);
}
Geom::Point stD, seD, enD;
double stL, seL, enL;
stD = a->getEdge(stB).dx;
seD = a->getEdge(i).dx;
enD = a->getEdge(enB).dx;
stL = sqrt (dot(stD,stD));
seL = sqrt (dot(seD,seD));
enL = sqrt (dot(enD,enD));
MiscNormalize (stD);
MiscNormalize (enD);
MiscNormalize (seD);
Geom::Point ptP;
int stNo, enNo;
ptP = a->getPoint(a->getEdge(i).st).x;
double this_dec;
if (do_profile && i2doc) {
double alpha = 1;
double x = (Geom::L2(ptP * (*i2doc) - Geom::Point(cx,cy))/radius);
if (x > 1) {
this_dec = 0;
} else if (x <= 0) {
this_dec = dec;
} else {
this_dec = dec * (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
}
} else {
this_dec = dec;
}
if (this_dec != 0)
done_something = true;
int usePathID=-1;
int usePieceID=0;
double useT=0.0;
if ( a->_has_back_data ) {
if ( a->ebData[i].pathID >= 0 && a->ebData[stB].pathID == a->ebData[i].pathID && a->ebData[stB].pieceID == a->ebData[i].pieceID
&& a->ebData[stB].tEn == a->ebData[i].tSt ) {
usePathID=a->ebData[i].pathID;
usePieceID=a->ebData[i].pieceID;
useT=a->ebData[i].tSt;
} else {
usePathID=a->ebData[i].pathID;
usePieceID=0;
useT=0;
}
}
if (dec > 0)
{
Path::DoRightJoin (this, this_dec, join, ptP, stD, seD, miter, stL, seL,
stNo, enNo,usePathID,usePieceID,useT);
a->swsData[i].stPt = enNo;
a->swsData[stB].enPt = stNo;
}
else
{
Path::DoLeftJoin (this, -this_dec, join, ptP, stD, seD, miter, stL, seL,
stNo, enNo,usePathID,usePieceID,useT);
a->swsData[i].stPt = enNo;
a->swsData[stB].enPt = stNo;
}
}
if (dec < 0)
{
for (int i = 0; i < numberOfEdges(); i++)
Inverse (i);
}
if ( _has_back_data ) {
for (int i = 0; i < a->numberOfEdges(); i++)
{
int nEd=AddEdge (a->swsData[i].stPt, a->swsData[i].enPt);
ebData[nEd]=a->ebData[i];
}
} else {
for (int i = 0; i < a->numberOfEdges(); i++)
{
AddEdge (a->swsData[i].stPt, a->swsData[i].enPt);
}
}
a->MakeSweepSrcData (false);
a->MakeSweepDestData (false);
return (done_something? 0 : shape_nothing_to_do);
}
.
[BEARBEITUNGEN]
@Simon Mourier - Tolle Arbeit. Der code war auch sauber und lesbar! Schöne Arbeit, sir. Ich habe ein paar Fragen für Sie, obwohl.
Erstens, was bedeutet eine positive Zahl für die Höhe dar? Ich dachte, dass es für die Offset-Methode, positiv sein würde "Anfang" und negativ wäre, "inset", aber dein Beispiel scheint das Gegenteil tun.
Zweiten, habe ich ein paar grundlegende Tests (nur die Erweiterung Ihrer Probe), und fand einige Merkwürdigkeiten.
Hier ist, was passiert mit dem "l" in cool, wenn der offset wächst (für solche einfachen Brief, sicher, es mag zu Problemen führen!).
...und der code zum reproduzieren eines:
private void Form1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath path = new GraphicsPath();
path.AddString("cool", new FontFamily("Arial"), 0, 200, new PointF(), StringFormat.GenericDefault);
GraphicsPath offset1 = path.Offset(32);
e.Graphics.DrawPath(new Pen(Color.Black, 1), path);
e.Graphics.DrawPath(new Pen(Color.Red, 1), offset1);
}
Schließlich, etwas ein wenig anders. Hier ist der "S" - Zeichen aus Wingdings (erscheint wie eine Träne):
Hier ist der code:
private void Form1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath path = new GraphicsPath();
path.AddString("S", new FontFamily("Wingdings"), 0, 200, new PointF(), StringFormat.GenericDefault);
GraphicsPath offset1 = path.Offset(20);
e.Graphics.DrawPath(new Pen(Color.Black, 1), path);
e.Graphics.DrawPath(new Pen(Color.Red, 1), offset1);
}
Mann, das ist so nah, es macht mich wollen, zu Weinen. Es funktioniert immer noch nicht, obwohl.
Denke ich, was würde es zu beheben ist, um zu sehen, wenn der eingesetzte Vektoren schneiden, und stoppen insetting über diesen Punkt. Wenn der eingesetzte Betrag ist so groß (oder der Pfad so klein), dass nichts übrig geblieben ist, der Pfad sollte verschwinden (zu null), anstelle der Umkehrung auf sich selbst und re-erweitern.
Wieder, ich bin nicht klopfen, was Sie getan haben, in irgendeiner Weise, aber ich Frage mich, ob Sie wissen, was könnte Los sein mit diesen Beispielen.
(PS - habe ich das "this" Schlüsselwort, um es eine extension-Methode, so dass Sie möglicherweise benötigen, rufen Sie den code mit der Methode(Parameter) notation dieser Proben laufen)
.
@RAN
Ran kommen mit einem ähnlichen Ausgang, durch die erneute Nutzung der GraphicsPath-native Methoden. Mann, das ist hart. Beide sind so nah.
Hier ist ein Screenshot der beiden Beispiele, die Benutzung des Zeichens "S" aus Wingdings:
@Simon ist auf der linken Seite, @Lief auf der rechten Seite.
Hier ist die gleiche tear drop "S" - Zeichen nach einem "Einschub" in Inkscape. Der Einschub ist sauber:
Übrigens, hier ist der code für die @Rans-test:
private void Form1_Paint(object sender, PaintEventArgs e)
{
GraphicsPath path = new GraphicsPath();
path.AddString("S", new FontFamily("Wingdings"), 0, 200, new PointF(), StringFormat.GenericDefault);
e.Graphics.DrawPath(new Pen(Color.Black, 1), path);
GraphicsPath offset1 = path.Shrink(20);
e.Graphics.DrawPath(new Pen(Color.Red, 1), offset1);
}
- Vielleicht könnten Sie schreiben Inkscape-C++ - code und jemand, der weiß, C++ wird in der Lage sein zu helfen.
- Sicher. Ich werde nach den "core" - Funktion von Inkscape, aber es kann durchaus ein paar Abhängigkeiten.
- Möchten Sie diese arbeiten auf Pfaden im Allgemeinen, oder nur Wege, die vorher verbreitert worden? Ich glaube nicht, dass es möglich sein wird, um "rückgängig", eine Erweiterung der transformation und bekomme wieder den ursprünglichen Weg, da ein Teil der widen Verhalten ist die approximation von Kurven durch eine Serie von Liniensegmenten.
- Hallo Ben - gute Frage. Nein, das sind Pfade, die vorher nicht "verbreitert", und keine ähnlichkeit mit die .Widen-Methode erforderlich ist.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Hier ist eine nette alternative. Es ist nicht so anspruchsvoll wie @Simon, aber es gibt schöne Ergebnisse (die noch weiter verbessert werden kann), mit viel einfacherem code.
Die Idee ist die Wiederverwendung der vorhandenen Funktionalität von
GraphicsPath.Widen
um die Punkte zu Holen.Wenn wir rufen
Widen
auf eineGraphicsPath
aus n geschlossenen Figuren, die daraus resultierende Pfad hat 2n Kanten. Eine äußere und eine innere Kante für jedes original Bild.So, ich erstellen Sie einen temporären Pfad, erweitern Sie, und kopieren Sie nur die inneren Kanten.
Hier der code:
Und hier ist ein Beispiel:
Zugegeben, meine Lösung hat auch zu unerwünschten Artefakten bei der offset ist groß genug, um die Ursache der Form zum selbst schneiden.
EDIT:
Kann ich leicht erkennen, alle Schnittpunkte in O(n^2), oder mit etwas Anstrengung - erkennen Sie in O(n logn), einen sweep-line-Algorithmus (n wird die Anzahl der Punkte).
Aber sobald ich Sie gefunden hab der Kreuzung Punkte, ich bin mir nicht sicher, wie Sie entscheiden, welche Teile des Pfades zu entfernen. Hat jemand eine Idee? 🙂
EDIT 2:
Eigentlich, dass wir nicht wirklich brauchen, um zu finden, die Schnittpunkte der Figuren.
Was wir tun können, ist, Scannen Sie alle Punkte auf der Abbildung. Einmal fanden wir eine Stelle, die entweder außerhalb der ursprünglichen Abbildung, oder zu nah an eine Kante des ursprünglichen Abbildung, dann haben wir es zu beheben.
Um fix einen Punkt, schauen wir auf die Kante zwischen diesem Punkt und dem vorherigen, und wir müssen schneiden diese Kante, so dass es nun in einen neuen Punkt, auf den richtigen Abstand von der original-Abbildung.
Ich habe einige Experimente mit einer ungefähren dieses Algorithmus (mit einem groben, aber einfachen Algorithmus, bei dem ich entfernt die "off" - Punkte völlig, anstatt zu verschieben, zu verkürzen, Ihre Kante, und ich überprüfte die Entfernungen zu Punkten auf der original-Abbildung statt, um die Kanten auf es). Habe dadurch einige schöne Ergebnisse erzielen, entfernen die meisten der unerwünschten Artefakten.
Zur Umsetzung der vollständigen Lösung würde wohl einige Stunden dauern...
EDIT 3:
Aber immer noch weit entfernt von perfekt, ich habe meine verbesserte Lösung in einer separaten Antwort.
OK, ich glaube, ich habe eine Führung für Euch... aber Ihr in eine völlig andere Richtung.
Sowieso, erkannte ich, dass ein "sub-Pfad" zu einem größeren Pfad tatsächlich schrumpft (Einsätze) während einer
.Widen
Betrieb, also habe ich beschlossen, um zu sehen, ob es etwas gab, was fruchtbar auf diesem Weg (kein Wortspiel beabsichtigt).Wirklich, die Idee hier ist, um
.Widen
den Pfad... von außen!Was ist, wenn wir nahmen die original -
GraphicsPath
und 'verpackt' in einem größerenRectangle
(tut einInflate
von 10 auf der.GetBounds
desGraphicsPath
sollte für eine einfache wrapper).Dann der wrapper wird zuerst Hinzugefügt, und die wahre
GraphicsPath
ist das so ein sub-Pfad zu diesem. Die ganze Sache bekommt dann eine.Widen
, und schließlich, einen neuenGraphicsPath
ist von Grund auf neu erstellt, mit der.PathPoints
und.PathTypes
der verbreiterten Weg, der entfernt die nutzlosen wrapper (zum Glück, dieGraphicsPath
akzeptiertPathPoints
undPathTypes
in einem der Konstruktor-überladungen).Werde ich im Büro sein, für den rest des Tages, so kann ich dies nicht sehen zu Ende, aber hier ist die führen.
Legen Sie einfach diesen code in eine regelmäßige ol' form:
Aus, dass einfach Experimentieren, werden Sie sehen, dass der Ziel-Pfad (der Kreis) hat jetzt die schwer Einsetzen (in rot)!
Es hat auch einige andere Scheiße, dass ich nicht wirklich verstehen, in der es (die erscheint auch auf dem Rechteck-wrapper), aber von der
PathPoints
undPathTypes
sollte es möglich sein, zum Durchlaufen des arrays und entfernen Sie den Müll, wenn die Jungfrau GraphicsPath erzeugt (oder finden Sie heraus, wo Sie, dass die junk kommt aus und verhindern, dass es passiert). Dann wieder die neue, saubereGraphicsPath
.Diese Technik vermeidet die komplexe Mathematik, aber es ist ein bisschen eine lange erschossen.