2012-08-13 13 views
9

Sto lavorando a un progetto utilizzando un canvas JavaScript e devo essere in grado di agganciare il cursore a una certa distanza da un poligono. Posso già fare uno snap al poligono stesso, ma ho bisogno di avere il cursore più lontano.Ridimensionare un poligono in modo che i bordi corrispondano a

Per quanto ne so, il modo migliore per farlo è ridimensionare il poligono e farlo scattare, ma quando scala il poligono la distanza tra i bordi del vecchio poligono e i bordi del nuovo poligono sempre.

Ecco un esempio del problema:

enter image description here enter image description here

Edit: Il grigio rappresenta il poligono originale, il rosso è quello che sto ottenendo, se mi scala il poligono normalmente, e la verde è quello che sto cercando di realizzare

Ho già provato a tradurre il poligono all'origine e moltiplicando per un fattore di scala, ma non riesco a ridimensionare ogni spigolo di una distanza specifica.

+0

Si prega di spiegare la vostra immagine, quali parti fare il rosso e il verde rappresentano? – Hogan

+0

Grazie. Modificato. – davey555

+0

Puoi pubblicare il codice che usi per il poligono, la traduzione e il ridimensionamento? E cosa intendi per "non sempre combaciano"? In quale situazione fallisce? –

risposta

1

posso offrire una soluzione che utilizza JSTS libreria (porto JavaScript del JTS). La libreria ha metodi per gonfiare/sgonfiare (sfalsare) di poligoni.

È possibile impostare il cappuccio e gli stili di join se si desidera ottenere il poligono gonfiato con un diverso tipo di bordi. L'unica cosa che dovete fare è quello di convertire si poligono coordinate ad un JSTS coordinata che è davvero semplice:

function vectorCoordinates2JTS (polygon) { var coordinates = []; for (var i = 0; i < polygon.length; i++) { coordinates.push(new jsts.geom.Coordinate(polygon[i].x, polygon[i].y)); } return coordinates; }

Una volta aver convertito le coordinate si può gonfiare il poligono:

function inflatePolygon(poly, spacing) { 
    var geoInput = vectorCoordinates2JTS(poly); 
    geoInput.push(geoInput[0]); 

    var geometryFactory = new jsts.geom.GeometryFactory(); 

    var shell = geometryFactory.createPolygon(geoInput); 
    var polygon = shell.buffer(spacing); 
    //try with different cap style 
    //var polygon = shell.buffer(spacing, jsts.operation.buffer.BufferParameters.CAP_FLAT); 

    var inflatedCoordinates = []; 
    var oCoordinates; 
    oCoordinates = polygon.shell.points.coordinates; 

    for (i = 0; i < oCoordinates.length; i++) { 
    var oItem; 
    oItem = oCoordinates[i]; 
    inflatedCoordinates.push(new Vector2(Math.ceil(oItem.x), Math.ceil(oItem.y))); 
    } 
    return inflatedCoordinates; 
} 

con il codice che ho postato su jsFiddle otterrete qualcosa di simile:

enter image description here

Ulteriori informazioni: Io di solito uso questo tipo di gonfiaggio/sgonfiaggio (un po 'modificato) per impostare i limiti con raggio sui poligoni che sono disegnati su una mappa (con Leaflet o Google maps). Basta convertire le coppie lat, lng in coordinate JSTS e tutto il resto è lo stesso. Esempio:

enter image description here

+0

Wow è bello, vorrei averlo avuto 3 anni fa ... – davey555

2

Un modo è quello di trovare la distanza tra ogni lato del poligono e il punto del cursore e mantenere il più piccolo.

Per calcolare la distanza tra un punto e un segmento di linea, proiettare il punto sulla linea di supporto; se la proiezione cade tra i punti finali, la soluzione è la distanza punto-a-linea; in caso contrario, la soluzione è la distanza dall'endpoint più vicino.

Questo è facilmente calcolato utilizzando il calcolo vettoriale.

3

ISTM che quello che stai cercando è un algoritmo o libreria di compensazione di poligoni.
Vedi An algorithm for inflating/deflating (offsetting, buffering) polygons

+0

Qualche idea per eseguire la compensazione del poligono in Javascript? –

+1

La libreria java JTS (Java Topology Suite) esegue la compensazione (sebbene si chiami il buffering in quel punto). Vedi https://sourceforge.net/projects/jts-topo-suite/ –

+0

È Java, non Javascript e anche il processo di porting è necessario. Come si confronta con Clipper? –

5

Ho fatto una jsFiddle che per un dato poligono, calcola un poligono esterno che spero soddisfa le vostre esigenze. Ho messo la matematica dietro in questo pdf document.

Aggiornamento: codice per le linee verticali.

function Vector2(x, y) 
{ 
    this.x = x; 
    this.y = y; 
} 

function straight_skeleton(poly, spacing) 
{ 
    // http://stackoverflow.com/a/11970006/796832 
    // Accompanying Fiddle: http://jsfiddle.net/vqKvM/35/ 

    var resulting_path = []; 
    var N = poly.length; 
    var mi, mi1, li, li1, ri, ri1, si, si1, Xi1, Yi1; 
    for(var i = 0; i < N; i++) 
    { 
     mi = (poly[(i+1) % N].y - poly[i].y)/(poly[(i+1) % N].x - poly[i].x); 
     mi1 = (poly[(i+2) % N].y - poly[(i+1) % N].y)/(poly[(i+2) % N].x - poly[(i+1) % N].x); 
     li = Math.sqrt((poly[(i+1) % N].x - poly[i].x)*(poly[(i+1) % N].x - poly[i].x)+(poly[(i+1) % N].y - poly[i].y)*(poly[(i+1) % N].y - poly[i].y)); 
     li1 = Math.sqrt((poly[(i+2) % N].x - poly[(i+1) % N].x)*(poly[(i+2) % N].x - poly[(i+1) % N].x)+(poly[(i+2) % N].y - poly[(i+1) % N].y)*(poly[(i+2) % N].y - poly[(i+1) % N].y)); 
     ri = poly[i].x+spacing*(poly[(i+1) % N].y - poly[i].y)/li; 
     ri1 = poly[(i+1) % N].x+spacing*(poly[(i+2) % N].y - poly[(i+1) % N].y)/li1; 
     si = poly[i].y-spacing*(poly[(i+1) % N].x - poly[i].x)/li; 
     si1 = poly[(i+1) % N].y-spacing*(poly[(i+2) % N].x - poly[(i+1) % N].x)/li1; 
     Xi1 = (mi1*ri1-mi*ri+si-si1)/(mi1-mi); 
     Yi1 = (mi*mi1*(ri1-ri)+mi1*si-mi*si1)/(mi1-mi); 
     // Correction for vertical lines 
     if(poly[(i+1) % N].x - poly[i % N].x==0) 
     { 
      Xi1 = poly[(i+1) % N].x + spacing*(poly[(i+1) % N].y - poly[i % N].y)/Math.abs(poly[(i+1) % N].y - poly[i % N].y); 
      Yi1 = mi1*Xi1 - mi1*ri1 + si1; 
     } 
     if(poly[(i+2) % N].x - poly[(i+1) % N].x==0) 
     { 
      Xi1 = poly[(i+2) % N].x + spacing*(poly[(i+2) % N].y - poly[(i+1) % N].y)/Math.abs(poly[(i+2) % N].y - poly[(i+1) % N].y); 
      Yi1 = mi*Xi1 - mi*ri + si; 
     } 

     //console.log("mi:", mi, "mi1:", mi1, "li:", li, "li1:", li1); 
     //console.log("ri:", ri, "ri1:", ri1, "si:", si, "si1:", si1, "Xi1:", Xi1, "Yi1:", Yi1); 

     resulting_path.push({ 
      x: Xi1, 
      y: Yi1 
     }); 
    } 

    return resulting_path; 
} 


var canvas = document.getElementById("Canvas"); 
var ctx = canvas.getContext("2d"); 

var poly = [ 
    new Vector2(150, 170), 
    new Vector2(400, 120), 
    new Vector2(200, 270), 
    new Vector2(350, 400), 
    new Vector2(210, 470) 
]; 

draw(poly); 
draw(straight_skeleton(poly, 10)); 

function draw(p) { 
    ctx.beginPath(); 
    ctx.moveTo(p[0].x, p[0].y); 
    for(var i = 1; i < p.length; i++) 
    { 
     ctx.lineTo(p[i].x, p[i].y); 
    } 
    ctx.strokeStyle = "#000000"; 
    ctx.closePath(); 
    ctx.stroke(); 
} 

Un poligono viene inserito in una matrice di oggetti punto.

La funzione draw(p) disegna il poligono p sulla tela.

Il poligono specificato è in array poly, l'esterno nell'array poly.

spacing è la distanza tra i poligoni (come lungo le frecce nel diagramma verde)


seguito il commento di Angus Johnson, ho prodotto alcuni altri violini per mostrare i problemi che solleva. Questo problema è molto più difficile di quanto pensassi.

+1

Ho avuto una rapida occhiata al codice e all'ISTM che compenserà correttamente solo i poligoni più semplici. Ad esempio, non riesco a vedere alcun codice per gestire angoli convessi molto acuti in cui i vertici possono spostare distanze esponenziali rispetto alla distanza di offset. Inoltre, con angoli concavi acuti con lati relativamente corti, è necessario un codice per rimuovere le autointersecazioni risultanti dalla semplice compensazione. –

+0

@Angus Johnson Ho appena provato con le condizioni specificate e vedo cosa intendi. Grazie per averlo indicato. Ci penserò un po 'di più. Forse davey555 commenterebbe se queste condizioni potrebbero insorgere nella sua applicazione. – jing3142

6

ho fatto un porto javascript di Clipper e con esso si può fare la scala nel modo desiderato.

Questo è un esempio di poligono di gonfiaggio:

enter image description here

Controllare il LIVE DEMO di Javascript Clipper.

e ottenere il file clipper.js da https://sourceforge.net/projects/jsclipper/.

Full code example di come spostare i poligoni e disegnarli su una tela html5.

L'opposto (sgonfiando) è anche possibile, se necessario:

Offsetting polygons

Problemi correlati