2014-10-21 12 views
13

Gibt es eine bekannte Methode, um den Schnittpunkt eines SVG-Pfades mit sich selbst zu finden?? Nehmen wir zum Beispiel das kaufmännische Und-Zeichen &, es ist eine Linie, die sich an zwei Punkten schneidet.Schnittpunkt des SVG-Pfades finden

Ich bin auf eine intersection library gestoßen, aber es scheint zu sprechen von zwei Formen schneiden, anstatt sich selbst zu schneiden.

Ich bin vertraut mit d3, so dass jede javascriptbasierte Antwort wäre toll, aber auch glücklich zu hören, welche mathematische Methode verwendet werden könnte.

Dank

+0

Hoffentlich ein Mathe geneigt Person wird kommen durch und beantworten Sie diese gut Frage. In der Zwischenzeit ist hier ein Gedanke: Wenn Sie den Pfad als eine Reihe von geraden (nicht kollinearen) Segmenten ausdrücken, dann müssen Sie nur überprüfen, ob ein Segment mit einem der anderen Segmente schneidet (mit Ausnahme der 2 Segmente, mit denen es verbunden ist)). Das Finden der [Kreuzung von zwei Segmenten] (http://en.wikipedia.org/wiki/Line%E2%80%93line_intersection) ist ziemlich direkt. Wenn der Pfad jedoch aus Hunderten von Segmenten besteht, kann dies rechenintensiv werden. – meetamit

+0

Um zu beginnen, sind das d3-Quadtree-Objekt und die Methoden nützlich, um Elemente zu finden, die geometrisch nahe beieinander liegen. [Hier ist eine Antwort, die ich mit Quadtree geschrieben habe, um überlappende rechteckige Etiketten zu vermeiden] (http://stackoverflow.com/a/21998197/3128209). Um mit einem einzelnen Pfad zu arbeiten, müssen Sie zunächst den Pfad in einzelne Segmente aufteilen und jeweils die Begrenzungsbox finden. Verwenden Sie dann den Quadtree, um überlappende BBoxen zu identifizieren. Wenn Ihr Pfad nur gerade Linien enthält, ist das Testen auf Schnittpunkte einfach, aber für beliebige Kurven würde es schwierig werden. – AmeliaBR

Antwort

14

Sie haben Recht - die Bibliothek von Kevin Lindsey (@thelonious) scheint hier die Arbeit zu tun.

Sie sagen, die Bibliothek für Kreuzungen zwischen zwei Instanzen derselben Form zu suchen, Vector2D Objekte Streifen aus, und was übrig bleibt, sind zwei Sätze der gleichen Schnittpunkte (Point2D Typ Objekte in seiner Bibliothek).

Dies ist der Hauptteil:

var pathEl = path.node(); 
var intersections = []; 

// Kevin Lindsey's library 
var shape1 = new Path(pathEl); 
var overlays = Intersection.intersectShapes(shape1, shape1); 

for (i in overlays.points) { 
    if (overlays.points[i].getName() == "Point2D") { 
     intersections.push(overlays.points[i]); 
    } 
} 

Voll eingebettet Beispiel unten:

var ampersand = "M 72.184621,99.39089 C 68.398038,95.61996 48.405425,73.700329 50.716835,49.985704 C 52.990823,26.655017 76.884556,12.578576 98.97427,11.448114 C 119.34404,10.405671 142.83345,16.156173 152.28457,35.843184 C 162.40413,56.922579 150.99532,81.842705 134.24772,94.48352 C 128.42088,98.881524 127.0609,99.082849 118.46055,102.84216 C 106.06795,108.25911 93.590914,113.54803 80.869078,118.1294 C 54.582831,127.59557 34.539139,149.03858 35.259701,178.23878 C 35.916374,204.84994 59.631137,225.67546 85.210802,229.70364 C 112.43115,233.99018 134.41358,229.54707 153.1347,208.67628 C 161.17912,199.70814 177.58763,184.99294 185.76751,176.14503 C 200.25035,160.47941 207.7442,147.82465 213.06419,126.69158 C 216.66826,112.37483 192.54569,115.67347 196.78314,103.62942 C 222.23036,100.69638 247.81229,99.84462 273.34564,97.96536 C 277.34887,109.81154 263.97786,106.33066 246.61613,121.01207 C 227.0104,137.59107 217.88679,151.73768 201.01195,170.86236 L 189.11358,184.34708 C 181.10521,193.42317 167.95634,207.85044 159.78314,216.77784 C 142.32024,235.85217 126.21297,247.41796 100.27427,252.39343 C 72.543606,257.71262 39.651129,254.69839 20.122962,231.94973 C -0.62641014,207.77846 -4.0848351,167.90434 18.462826,143.66847 C 33.171306,127.85873 41.031184,120.17885 60.724466,112.09432 C 76.147466,105.76283 100.05575,99.431353 112.29677,94.160526 C 139.69178,82.364582 140.40896,53.478721 127.50818,32.380115 C 116.44184,14.281646 83.908653,15.752833 77.904904,37.000557 C 72.689417,55.458561 80.089538,67.982449 91.37226,80.93907 L 187.58994,191.43156 C 199.42503,205.63979 217.24414,228.88851 237.39579,232.51125 C 250.72342,234.90721 267.9319,228.93995 277.27793,220.4821 C 282.25334,229.49138 275.03265,236.84049 269.43939,242.49659 C 251.14471,260.99681 219.58458,257.23653 199.30993,242.48439 C 187.00911,233.53413 178.95611,227.18492 167.95716,215.15746 L 72.184621,99.39089 z "; 
 

 
var svg = d3.select("body").append("svg") 
 
    .attr("width", 300) 
 
    .attr("height", 300); 
 

 
var path = svg.append("svg:path") 
 
      .attr("d",ampersand) 
 
      .style("stroke-width", 2) 
 
      .style("stroke", "steelblue") 
 
      .style("fill", "none"); 
 

 
// Taken from https://stackoverflow.com/questions/332422/how-do-i-get-the-name-of-an-objects-type-in-javascript 
 
Object.prototype.getName = function() { 
 
    var funcNameRegex = /function (.{1,})\(/; 
 
    var results = (funcNameRegex).exec((this).constructor.toString()); 
 
    return (results && results.length > 1) ? results[1] : ""; 
 
}; 
 

 
// Taken from https://stackoverflow.com/questions/9229645/remove-duplicates-from-javascript-array 
 
function uniq(a) { 
 
    var prims = {"boolean":{}, "number":{}, "string":{}}, objs = []; 
 

 
    return a.filter(function(item) { 
 
     var type = typeof item; 
 
     if(type in prims) 
 
      return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true); 
 
     else 
 
      return objs.indexOf(item) >= 0 ? false : objs.push(item); 
 
    }); 
 
} 
 

 
var pathEl = path.node(); 
 
var intersections = []; 
 

 
// Kevin Lindsey's library 
 
var shape1 = new Path(pathEl); 
 
var overlays = Intersection.intersectShapes(shape1, shape1); 
 

 
for (i in overlays.points) { 
 
    if (overlays.points[i].constructor.name == "Point2D") { 
 
     intersections.push(overlays.points[i]); 
 
    } 
 
} 
 

 
// The path will record 2 points for each intersection, so deduping is necessary 
 
var deduped_intersections = uniq(intersections); 
 

 
var circles = svg.selectAll("circle") 
 
       .data(deduped_intersections) 
 
       .enter() 
 
       .append("circle"); 
 

 
var circleAttributes = circles 
 
         .attr("cx", function (d) { return d.x; }) 
 
         .attr("cy", function (d) { return d.y; }) 
 
         .attr("r", "3") 
 
         .style("fill", "red");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 
 
<script src="http://www.kevlindev.com/gui/2D.js"></script>

+1

Schön! Es ist immer wieder toll zu entdecken, dass jemand anderes die schwierigen Dinge getan hat, aber danke, dass du alles in einem so eleganten Ausschnitt zusammengefasst hast. Ich bin mir sicher, dass eine Zeit kommen wird, in der ich eine ähnliche Funktion brauche. – AmeliaBR

+0

Danke für ein tolles Arbeitsbeispiel mccannf. Ich tauschte deinen Ampersand-Pfad mit meinem eigenen, obwohl keine Point2D-Schnittpunkte gefunden wurden http://bl.ocks.org/danharr/3931242a8b5a1a76d2a3 nur Vector2D co-ords http://bl.ocks.org/danharr/2002b2fd98cb09f71522 Gibt es einen Gewisse Weise muss der ursprüngliche Svg-Pfad sein, damit dieser Ansatz funktioniert? –

+0

@DanHarrington - ooops :(Sie haben das Beispiel gefunden, das nicht funktioniert, sieht so aus, als ob es die Schnittpunktbibliothek als Kandidat ungültig macht, um das zu lösen ... Ich habe gesucht, und die echten Schnittpunkte sind in diesen Vector2D Koordinaten, aber es gibt keine Möglichkeit, sie aus den falschen Übereinstimmungen herauszufiltern. – mccannf