Come creare un tratto a pennino a punta larga trascinando una forma fissa lungo un percorso
Per creare un tratto a pennino a punta larga, definisci un pennino rettangolare stretto, mantienine costante l’angolo e traslalo lungo un percorso. Il tratto è l’unione di tutte le posizioni occupate dal rettangolo. Il suo spessore cambia in base alla direzione del movimento, anche se il pennino non cambia mai dimensione.
Questa guida mostra come costruire tale geometria per un disegno vettoriale o un accento su un personaggio. Definirai il pennino, lo trascinerai in modo continuo lungo una polilinea e genererai SVG con un programma Python completo. La costruzione fondamentale evita gli spazi vuoti che possono apparire quando si simula un pennello con timbri separati.
Cosa significa trascinare una forma fissa?
Separa il disegno in due input: un percorso che descrive dove si sposta il punto di riferimento del pennino e un’impronta che descrive l’area coperta dal pennino in una posizione. Posiziona il punto di riferimento dell’impronta all’origine. In ogni posizione del percorso, somma tale posizione a ogni punto dell’impronta.
Matematicamente, l’insieme risultante è S = {p + n | p belongs to P, n belongs to N}, dove P è il percorso e N è il pennino. Si tratta di una somma di Minkowski, secondo la definizione punto per punto nel manuale utente di CGAL sulle somme di Minkowski 2D.
In questa costruzione, “fisso” significa che sia l’impronta sia il suo orientamento rimangono costanti nelle coordinate del disegno. Il pennino si trasla senza ruotare per seguire il percorso. Mantenere il punto di riferimento al centro del rettangolo fa sì che il percorso descriva il centro del pennino in movimento.
In che modo l’angolo del pennino e la direzione del movimento determinano lo spessore?
Scegli tre parametri: la dimensione lunga del rettangolo w, la dimensione corta h e l’angolo α del lato lungo. Un valore positivo piccolo di h dà al pennino uno spessore minimo visibile. Un pennino ideale a forma di segmento, invece, ha spessore zero e può lasciare un segno di area nulla quando si muove esattamente nella propria direzione.
Per un segmento rettilineo con angolo di direzione θ, la larghezza del pennino rettangolare misurata perpendicolarmente alla direzione del movimento è:
width across the stroke = w × |sin(θ − α)| + h × |cos(θ − α)|
Questa formula si ottiene proiettando le due dimensioni perpendicolari del rettangolo sulla normale al percorso. Descrive un segmento rettilineo o la proiezione locale dell’impronta lungo una curva liscia; le parti sovrapposte di un tratto curvo possono rendere più larga la sua sagoma complessiva.
Per un pennino d’esempio di 24 unità per 3 unità, un movimento parallelo al lato lungo produce una striscia larga 3 unità. Un movimento perpendicolare produce una striscia larga 24 unità. Con una differenza di 45°, la larghezza è circa (24 + 3) / √2, ovvero 19,09 unità. Sono dimensioni calcolate, non risultati misurati con un pennello.
Usa queste tre direzioni come test di calibrazione prima di disegnare una curva complessa. Permettono di verificare che angolo e dimensioni si comportino come previsto senza dover interpretare diverse curve sovrapposte.
Come si può trascinare la forma in modo continuo senza lasciare spazi tra i timbri?
Per un pennino convesso che si muove lungo un singolo segmento rettilineo, prendi una copia del pennino in ciascun estremo e calcola l’inviluppo convesso dei vertici combinati. Tale inviluppo corrisponde esattamente all’area spazzata durante la traslazione lungo il segmento.
Il motivo è semplice: ogni posizione intermedia è un’interpolazione lineare tra le posizioni agli estremi. Poiché il pennino è convesso, l’inviluppo contiene esattamente tali posizioni interpolate. Questa costruzione per segmento deriva direttamente dalla definizione di somma di Minkowski, non è una funzione separata del pennello.
Per un rettangolo, ogni segmento fornisce solo otto punti candidati: quattro angoli all’inizio e quattro alla fine. Calcola il loro inviluppo, ripeti l’operazione per ogni coppia consecutiva di punti del percorso e combina i poligoni risultanti. Le scansioni adiacenti condividono l’intera posizione del pennino nel punto finale comune, quindi la copertura geometrica è continua.
Non calcolare un unico inviluppo convesso attorno all’intero percorso. Riempirebbe gli spazi interni alle curve, cancellando le rientranze volute. L’inviluppo va calcolato per ogni singolo segmento; il tratto completo è l’unione dei relativi inviluppi.
Un pennino concavo richiede un trattamento diverso. Calcolarne l’inviluppo riempirebbe le rientranze. Scomponilo in parti convesse, trascina ciascuna parte e combina i risultati. CGAL documenta il relativo approccio basato su scomposizione e unione per le somme di Minkowski di poligoni non convessi.
Come si implementa la scansione in Python?
Il seguente programma completo richiede Python 3 e la sua libreria standard. Definisce un percorso di esempio con cinque punti, costruisce un pennino rettangolare e stampa un documento SVG contenente quattro poligoni pieni corrispondenti alle scansioni. Salvalo come broad_edge.py, quindi esegui python broad_edge.py > stroke.svg per creare il disegno.
Le coordinate seguono la convenzione SVG usata qui: x cresce verso destra e y verso il basso. Di conseguenza, in questo programma un angolo positivo inclina il pennino in senso orario sullo schermo. Altre interfacce di disegno possono usare una convenzione diversa per gli angoli.
from math import cos, sin, radians
def hull(points):
pts = sorted(set(points))
if len(pts) <= 1:
return pts
def cross(o, a, b):
return ((a[0] - o[0]) * (b[1] - o[1])
- (a[1] - o[1]) * (b[0] - o[0]))
def half(sequence):
out = []
for p in sequence:
while len(out) >= 2 and cross(out[-2], out[-1], p) <= 0:
out.pop()
out.append(p)
return out
return half(pts)[:-1] + half(reversed(pts))[:-1]
def sweep(path, width=24, thickness=3, angle=30):
if not path or width <= 0 or thickness <= 0:
raise ValueError(
'Provide a nonempty path and positive nib dimensions'
)
a = radians(angle)
u = (cos(a), sin(a))
v = (-sin(a), cos(a))
nib = [
(s * width * u[0] / 2 + t * thickness * v[0] / 2,
s * width * u[1] / 2 + t * thickness * v[1] / 2)
for s, t in [(-1, -1), (1, -1), (1, 1), (-1, 1)]
]
pairs = list(zip(path, path[1:])) or [(path[0], path[0])]
return [
hull([(p[0] + q[0], p[1] + q[1])
for p in (start, end) for q in nib])
for start, end in pairs
]
path = [(40, 45), (85, 125), (140, 150), (195, 110), (245, 45)]
polygons = sweep(path)
all_points = [p for polygon in polygons for p in polygon]
x0, y0 = (min(p[i] for p in all_points) - 5 for i in (0, 1))
x1, y1 = (max(p[i] for p in all_points) + 5 for i in (0, 1))
svg = [
'<svg xmlns="http://www.w3.org/2000/svg" '
f'viewBox="{x0} {y0} {x1 - x0} {y1 - y0}">',
'<g fill="#23364d" stroke="none">'
]
for polygon in polygons:
coordinates = ' '.join(f'{x:.6f},{y:.6f}' for x, y in polygon)
svg.append(f'<polygon points="{coordinates}"/>')
svg.extend(['</g>', '</svg>'])
print('\n'.join(svg))
Per un singolo punto del percorso, il programma disegna una sola impronta del pennino. Anche punti consecutivi ripetuti producono un’impronta, senza richiedere il calcolo di una direzione. I percorsi vuoti e le dimensioni del pennino non positive generano un errore. Fornisci coppie di coordinate numeriche finite; questo esempio compatto non convalida i dati di percorsi importati.
La costruzione è stata eseguita con le coordinate di esempio: ha prodotto quattro poligoni di scansione con sei vertici ciascuno e l’SVG generato è stato analizzato correttamente come XML. Sono stati verificati anche i casi con un solo punto e con punti ripetuti. Queste verifiche riguardano la costruzione e la struttura del documento, non costituiscono una prova di rendering visivo.
Come gestire curve, angoli e estremità?
L’esempio è esatto per la sua polilinea, salvo gli effetti dell’aritmetica in virgola mobile e dell’arrotondamento delle coordinate. Per usare una curva di Bézier, approssimala prima con segmenti rettilinei, quindi applica la stessa scansione. Aggiungi segmenti nei punti in cui la curva piega bruscamente, invece di distribuirli uniformemente lungo il parametro della curva.
Scegli la tolleranza di approssimazione in base alla dimensione di output prevista. Per esempio, una deviazione di un quarto di pixel può essere un obiettivo iniziale per la visualizzazione, seguito da un controllo alla scala massima prevista. È una scelta ingegneristica esemplificativa, non una soglia di qualità universale. Suddividere ulteriormente un segmento già rettilineo non cambia l’area spazzata.
Mantieni separati i percorsi di tratti distinti. Passare in un’unica chiamata i punti di due tratti scollegati creerebbe una scansione di collegamento indesiderata. Genera ogni tratto separatamente e raccogli i relativi poligoni nell’output.
Alle estremità, il pennino mantiene la sua impronta completa. In corrispondenza di un angolo, le due scansioni adiacenti si sovrappongono. Questo modello non aggiunge giunzioni a spigolo né estremità arrotondate. Le normali proprietà di tratto di SVG definiscono costruzioni proprie per estremità e giunzioni; aggiungerle introdurrebbe geometria che va oltre questa scansione con pennino fisso. Consulta le definizioni SVG 2 di estremità e giunzioni dei tratti.
Quando è necessario unire i poligoni?
Il programma esporta poligoni pieni sovrapposti. Per un’anteprima a colore uniforme, rappresentano l’area coperta dalla scansione senza richiedere una libreria per l’unione di poligoni. Rimangono tuttavia oggetti separati e modificabili; il programma non calcola un unico contorno consolidato.
Se ti serve una sagoma pulita per successive modifiche al contorno, calcola l’unione booleana di tutti i poligoni dei segmenti. Conserva gli eventuali fori che rimangono nel risultato. Un percorso ad anello può circondare un’area non dipinta e riempire ogni contorno chiuso cambierebbe il tratto.
Anche la trasparenza richiede attenzione. Applicando fill-opacity parzialmente ai singoli poligoni sovrapposti, le aree di sovrapposizione possono risultare più scure. Per una traslucenza uniforme, applica l’opacità al gruppo che li contiene oppure unisci le geometrie prima di applicare l’opacità del riempimento. Nella sua specifica di rendering, SVG distingue l’opacità del riempimento dell’oggetto da quella del gruppo.
| Risultato desiderato | Passaggio successivo appropriato |
|---|---|
| Anteprima a colore uniforme | Mantieni i poligoni pieni della scansione |
| Anteprima con traslucenza uniforme | Applica l’opacità al gruppo che li contiene |
| Un’unica sagoma modificabile | Calcola l’unione booleana dei poligoni della scansione |
| Pennino personalizzato concavo | Scomponi il pennino prima della scansione |
| Tratto curvo liscio | Approssima la curva, poi trascina la forma lungo i segmenti |
Come si individua la causa di un tratto imprevisto?
Inizia dalle direzioni di calibrazione, poi controlla il percorso e la costruzione dell’output. Modifica un input alla volta, così la causa di una differenza visibile resta chiara.
- Lo spessore rimane pressoché costante: controlla se il pennino ruota con il percorso. Il suo angolo deve rimanere fisso nelle coordinate del disegno.
- Compaiono spazi visibili tra i segni: sostituisci i timbri isolati con la costruzione dell’inviluppo per segmento.
- Una grossa forma a cuneo riempie l’interno di una curva: controlla se hai calcolato per errore un inviluppo attorno a più segmenti del percorso insieme.
- Le estremità sembrano arrotondate: controlla se è stato aggiunto un normale tratto o se si sta usando un’impronta diversa per il pennino.
- Una curva di origine liscia appare spigolosa: affina l’approssimazione polilineare nei punti in cui curva.
- Compaiono collegamenti imprevisti: mantieni i percorsi scollegati in chiamate di scansione separate.
Se stai confrontando il risultato con lo strumento Calligrafia di Inkscape, la relativa documentazione offre parametri utili da controllare: Fissaggio 100 mantiene fisso l’angolo, Assottigliamento 0 elimina le variazioni di larghezza basate sulla velocità e i controlli di pressione e inclinazione possono modificare larghezza e angolo. Disattiva questi input variabili quando esamini il comportamento di un pennino fisso. Queste impostazioni descrivono i controlli dello strumento; non dimostrano che il suo risultato sia identico a quello di questa costruzione rettangolare. Consulta la documentazione della barra degli strumenti Calligrafia di Inkscape.
Un tratto a punta larga affidabile parte da un percorso di calibrazione semplice e da un’impronta definita con chiarezza. Quando i test parallelo, perpendicolare e diagonale danno i risultati previsti, usa la stessa scansione per segmenti sul percorso scelto, affina le sezioni curve in base alla scala di output e unisci i poligoni quando il passaggio di modifica successivo richiede una sola sagoma.
