2020-08-15 15:21:42 +02:00
import math
import re
import inkex
from inkex import bezier
from inkex . paths import Path , CubicSuperPath
2021-06-02 23:30:37 +02:00
class BarrelDistorsion ( inkex . EffectExtension ) :
2021-04-15 17:03:47 +02:00
def add_arguments ( self , pars ) :
pars . add_argument ( " --lambda_coef " , type = float , default = - 5.0 , help = " command line help " )
2020-08-15 15:21:42 +02:00
def distort_coordinates ( self , x , y ) :
""" Method applies barrel distorsion to given points with distorsion center in center of image, selected to
Args :
x ( float ) : X coordinate of given point
y ( float ) : Y coordinate of given point
Returns :
tuple ( float , float ) : Tuple with X , Y distorted coordinates of given point
"""
x_u = ( x - self . x_c ) / ( self . width + self . height )
y_u = ( y - self . y_c ) / ( self . width + self . height )
x_d = x_u / 2 / ( self . q * y_u * * 2 + x_u * * 2 * self . q ) * ( 1 - math . sqrt ( 1 - 4 * self . q * y_u * * 2 - 4 * x_u * * 2 * self . q ) )
y_d = y_u / 2 / ( self . q * y_u * * 2 + x_u * * 2 * self . q ) * ( 1 - math . sqrt ( 1 - 4 * self . q * y_u * * 2 - 4 * x_u * * 2 * self . q ) )
x_d * = self . width + self . height
y_d * = self . width + self . height
x_d + = self . x_c
y_d + = self . y_c
return x_d , y_d
def split_into_nodes ( self , nodes_number = 1000 ) :
for id , node in self . svg . selected . items ( ) :
if node . tag == inkex . addNS ( ' path ' , ' svg ' ) :
p = CubicSuperPath ( node . get ( ' d ' ) )
new = [ ]
for sub in p :
new . append ( [ sub [ 0 ] [ : ] ] )
i = 1
while i < = len ( sub ) - 1 :
length = bezier . cspseglength (
new [ - 1 ] [ - 1 ] , sub [ i ] )
splits = nodes_number
for s in range ( int ( splits ) , 1 , - 1 ) :
new [ - 1 ] [ - 1 ] , next , sub [
i ] = bezier . cspbezsplitatlength (
new [ - 1 ] [ - 1 ] , sub [ i ] , 1.0 / s )
new [ - 1 ] . append ( next [ : ] )
new [ - 1 ] . append ( sub [ i ] )
i + = 1
node . set ( ' d ' , str ( CubicSuperPath ( new ) ) )
def effect ( self ) :
if re . match ( r ' g \ d+ ' ,
list ( self . svg . selected . items ( ) ) [ 0 ] [ 0 ] ) is not None :
raise SystemExit (
" You are trying to distort group of objects. \n This extension works only with path objects due to Inkscape API restrictions. \n Ungroup your objects and try again. "
)
self . split_into_nodes ( )
self . q = self . options . lambda_coef
2021-04-15 17:03:47 +02:00
if self . q == 0.0 :
inkex . errormsg ( " Invalid lambda coefficient. May not be exactly zero. " )
return
2020-08-15 15:21:42 +02:00
nodes = [ ]
for id , node in self . svg . selected . items ( ) :
if node . tag == inkex . addNS ( ' path ' , ' svg ' ) :
path = Path ( node . get ( ' d ' ) ) . to_arrays ( )
nodes + = path
nodes_filtered = [ x for x in nodes if x [ 0 ] != ' Z ' ]
x_coordinates = [ x [ - 1 ] [ - 2 ] for x in nodes_filtered ]
y_coordinates = [ y [ - 1 ] [ - 1 ] for y in nodes_filtered ]
self . width = max ( x_coordinates ) - min ( x_coordinates )
self . height = max ( y_coordinates ) - min ( y_coordinates )
self . x_c = sum ( x_coordinates ) / len ( x_coordinates )
self . y_c = sum ( y_coordinates ) / len ( y_coordinates )
for id , node in self . svg . selected . items ( ) :
if node . tag == inkex . addNS ( ' path ' , ' svg ' ) :
path = Path ( node . get ( ' d ' ) ) . to_arrays ( )
distorted = [ ]
first = True
for cmd , params in path :
if cmd != ' Z ' :
if first == True :
x = params [ - 2 ]
y = params [ - 1 ]
distorted . append (
[ ' M ' ,
list ( self . distort_coordinates ( x , y ) ) ] )
first = False
else :
x = params [ - 2 ]
y = params [ - 1 ]
distorted . append (
[ ' L ' , self . distort_coordinates ( x , y ) ] )
node . set ( ' d ' , str ( Path ( distorted ) ) )
if __name__ == ' __main__ ' :
2021-06-02 23:30:37 +02:00
BarrelDistorsion ( ) . run ( )