def xispline(pts, ams, eps=0.05, angle_on_straigth=True): """ Calcola i punti base di una linea spline con spigoli che approssima i punti passati in pts, inserendo un punto spigolo quando l'angolo tra due segmenti è maggiore di ams, fino a raggiungere precisione eps. Restituisce la lista dei punti e base e una lista con gli indici relativi ai punti spigolo. """ # Semplifica la linea in input (questo toglie anche i punti doppi) pts = geo2d.simplify(pts, eps/2) # Calcola gli indici dei punti spigolo interni sp = geo2d.getAnglePointsIndexes(pts, ams, angle_on_straigth) # Calcola il risultato finale res = [pts[0]] fsp = [] for i in xrange(0, len(sp)-1): s = geo2d.ispline(geo2d.Path(pts[sp[i]:sp[i+1]+1])) res.extend(s[1:]) fsp.append(len(res)-1) # Salva l'indice (in res!!) del punto spigolo del fsp[-1] # Elimina l'ultimo indice di spigolo auto_closed = geo2d.same(res[0], res[-1]) # Se il primo e l'ultimo punto sono uguali, cancello l'ultimo if auto_closed: del res[-1] fsp.insert(0,0) # In questo caso l'inizio e' sempre punto spigolo return res, fsp, auto_closed
def offset(pts, d, tipo="SPIGOLO_VIVO", soglia_unibili=1.0): # Semplifica la linea in input (questo toglie anche i punti doppi) pts = geo2d.simplify(pts, soglia_unibili/50.0) if max(abs(dd) for dd in d) < soglia_unibili*0.6: # Caso speciale per offset a distanza piccolissima: # calcola il risultato considerando semplicemente lo # spostamento punto a punto lungo la bisettrice delle # normali dei segmenti incidenti. La distanza usata # viene modificata per evitare di "tagliare le curve". # Il risultato finale e' una parallela a "spigolo vivo" # corretta a meno delle auto-intersezioni e/o saltelli. # In realtà esiste un caso in cui a questo punto ci sono ancora # dei punti doppi consecutivi, ovvero quello in cui siamo # rimasti con due punti coincidenti. In questo caso non è # chiaro cosa sia l'offset, per cui torniamo il path di partenza # come fa anche la offset di geo2dcpp if len(pts) < 3 and geo2d.same(pts[0], pts[1]): return [geo2d.Path(pts)] chiusa = abs(pts[0] - pts[-1]) < 0.0001 res = [] pts = geo2d.Path(pts) # Gli utilizzatori attuali della offset si aspettano che le curve # chiuse vengano sempre espanse verso l'esterno con offset positivo # e verso l'interno con offset negativo; ora, il lato scelto dalla # funzione offset dipende dal verso della curva, ma visto che non # vogliamo modificarlo solo per mantenere questa invariante, per # ottenere lo stesso risultato inverte il segno dell'offset richiesto. s = 1.0 if chiusa and geo2d.sarea(pts) > 0: s = -1.0 for dd in d: dd *= s off = [] for i in xrange(0, len(pts)): ddir = geo2d.nd(pts, i, dd, chiusa) off.append(pts[i] + ddir) res.append(geo2d.Path(off)) else: tipi = dict(SPIGOLO_VIVO=0, SMUSSATA=1) res = geo2d.offsetf(pts, d, tipi[tipo], soglia_unibili) return res
def interpola(pts, d): """ Data una spezzata aggiunge i punti necessari a fare in modo che la risultante abbia tutti i punti a distanza al più `d` tra un punto e il successivo. """ if d <= 0: raise ValueError pts = geo2d.simplify(pts, 1/100.0) res = [] res.append(pts[0]) for i in xrange(len(pts) - 1): delta = pts[i+1] - pts[i] n = 1 + int(abs(delta) / d) for j in xrange(n): t = (j + 1.0) / n res.append(pts[i] + t*delta) return geo2d.Path(res)
def complexSplineXP(pts, spigoli, levels=6, closed=0, iters=20, kk=1.62, tipo_spline="CALIGOLA"): """ Calcola una linea complessa: pts lista punti da interpolare spigoli lista degli indici dei punti spigolo levels=6 numero livelli di suddivisione tratti spline closed=false true se la linea e' chiusa iters=20 numero iterazioni per calcolo interpolazione tratti spline kk=1.62 coefficiente di rilassamento tratti spline Restituisce una lista di punti e una lista di tanti interi quanti sono gli elementi di pts che contiene l'indice del punto risultante associato per ciascuno dei punti in ingresso. In input, una linea chiusa deve essere passata *senza* duplicazione del primo/ultimo punto. Nel valore di ritorno, invece, il punto risulterà duplicato. FIXME: potremmo correggere questa asimmetria. La lista di mapping ritornata è sicuramente ordinata, ma può contenere duplicati: infatti, è possibile che due punti geometricamente sovrapposti nella linea di input corrispondano ad un solo punto nella spline di output. """ assert len(spigoli) <= len(pts), \ "Numero di spigoli maggiore di quello dei punti: %r > %r" % (len(spigoli), len(pts)) spigoli = sorted(spigoli) if not closed: # Se la linea e' aperta allora il primo e l'ultimo punto # sono sempre considerati punti spigolo if 0 not in spigoli: spigoli = [0] + spigoli if len(pts)-1 not in spigoli: spigoli = spigoli + [len(pts)-1] if not spigoli: # Linea morbida chiusa, usa una singola spline if tipo_spline == "CALCAD": pts.append(pts[0]) # aggiungo il primo punto in fondo res, ix = geo2d.oldSplineX(pts) else: res, ix = geo2d.splineX(pts, levels, closed, iters, kk) # else: if closed: spigoli = [spigoli[-1]] + spigoli i0 = spigoli.pop(0) res = [pts[i0]] ix = {i0: 0} while spigoli: i1 = spigoli.pop(0) pp = [pts[i0]] ii = [i0] i = (i0 + 1) % len(pts) while i != i1: pp.append(pts[i]) ii.append(i) i = (i + 1) % len(pts) pp.append(pts[i1]) ii.append(i1) if len(pp) == 2: # Tratto rettilineo ix[i1] = len(res) res.append(pts[i1]) else: # Tratto spline if tipo_spline == "CALCAD": if pp[0] == pp[-1]: # se punto iniziale a spigolo pp.append(pp[0]) # aggiungo (ancora) il primo punto in fondo ts, tix = geo2d.oldSplineX(pp) else: ts, tix = geo2d.splineX(pp, levels, 0, iters, kk) for t, i in zip(tix,ii): v = len(res) + t - 1 ix[i] = v res.extend(ts[1:]) i0 = i1 # Ruota per allineare risultato con pts (necessario solo # per spline chiuse con spigoli in cui il primo punto non # sia un punto spigolo) xx = ix[0] if xx > 0: res = res[xx:-1] + res[:xx+1] print 'res s p', len(res) for i in xrange(len(pts)): a = (ix[i] - xx) % len(res) ix[i] = a # A seguito della rotazione, due punti sovrapposti possono risultare # essere il primo e l'ultimo, nel qual caso ix non sarebbe più # ordinata. if ix[len(pts)-1] == 0: ix[len(pts)-1] = len(res)-1 # converte il dict in lista nix = [None] * len(pts) for i, v in ix.items(): nix[i] = v ix = nix sres = [res[0]] six = [ix[0]] # L'algoritmo di semplificazione per tratto funziona solo se ix è ordinata assert sorted(ix) == ix for i in xrange(1, len(ix)): if ix[i-1] != ix[i]: del sres[-1] s = res[ix[i-1]:ix[i]+1] assert len(s) > 0 sres.extend(geo2d.simplify(s)) six.append(len(sres) - 1) if closed: del sres[-1] sres.extend(geo2d.simplify(res[ix[-1]:])) return sres, six