@ -22,6 +22,7 @@ import pathlib
import gzip
import numpy
from numpy . typing import ArrayLike , NDArray
import fatamorgana
import fatamorgana . records as fatrec
from fatamorgana . basic import PathExtensionScheme , AString , NString , PropStringReference
@ -47,6 +48,10 @@ path_cap_map = {
#TODO implement more shape types?
def rint_cast ( val : ArrayLike ) - > NDArray [ numpy . int64 ] :
return numpy . rint ( val , dtype = numpy . int64 , casting = ' unsafe ' )
def build (
patterns : Union [ Pattern , Sequence [ Pattern ] ] ,
units_per_micron : int ,
@ -87,7 +92,7 @@ def build(
` fatamorgana . records . LayerName ` entries .
Default is an empty dict ( no names provided ) .
modify_originals : If ` True ` , the original pattern is modified as part of the writing
process . Otherwise , a copy is made and ` deepunlock ( ) ` - ed .
process . Otherwise , a copy is made .
Default ` False ` .
disambiguate_func : Function which takes a list of patterns and alters them
to make their names valid and unique . Default is ` disambiguate_pattern_names ` .
@ -109,7 +114,7 @@ def build(
annotations = { }
if not modify_originals :
patterns = [ p . deepunlock ( ) for p in copy . deepcopy ( patterns ) ]
patterns = copy . deepcopy ( patterns )
# Create library
lib = fatamorgana . OasisLayout ( unit = units_per_micron , validation = None )
@ -285,11 +290,13 @@ def read(
if isinstance ( element , fatrec . Polygon ) :
vertices = numpy . cumsum ( numpy . vstack ( ( ( 0 , 0 ) , element . get_point_list ( ) ) ) , axis = 0 )
annotations = properties_to_annotations ( element . properties , lib . propnames , lib . propstrings )
poly = Polygon ( vertices = vertices ,
layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
annotations = annotations ,
repetition = repetition )
poly = Polygon (
vertices = vertices ,
layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
annotations = annotations ,
repetition = repetition ,
)
pat . shapes . append ( poly )
@ -308,14 +315,16 @@ def read(
element . get_extension_end ( ) [ 1 ] ) )
annotations = properties_to_annotations ( element . properties , lib . propnames , lib . propstrings )
path = Path ( vertices = vertices ,
layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
repetition = repetition ,
annotations = annotations ,
width = element . get_half_width ( ) * 2 ,
cap = cap ,
* * path_args )
path = Path (
vertices = vertices ,
layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
repetition = repetition ,
annotations = annotations ,
width = element . get_half_width ( ) * 2 ,
cap = cap ,
* * path_args ,
)
pat . shapes . append ( path )
@ -323,12 +332,13 @@ def read(
width = element . get_width ( )
height = element . get_height ( )
annotations = properties_to_annotations ( element . properties , lib . propnames , lib . propstrings )
rect = Polygon ( layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
repetition = repetition ,
vertices = numpy . array ( ( ( 0 , 0 ) , ( 1 , 0 ) , ( 1 , 1 ) , ( 0 , 1 ) ) ) * ( width , height ) ,
annotations = annotations ,
)
rect = Polygon (
layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
repetition = repetition ,
vertices = numpy . array ( ( ( 0 , 0 ) , ( 1 , 0 ) , ( 1 , 1 ) , ( 0 , 1 ) ) ) * ( width , height ) ,
annotations = annotations ,
)
pat . shapes . append ( rect )
elif isinstance ( element , fatrec . Trapezoid ) :
@ -357,12 +367,13 @@ def read(
vertices [ 2 , 0 ] - = b
annotations = properties_to_annotations ( element . properties , lib . propnames , lib . propstrings )
trapz = Polygon ( layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
repetition = repetition ,
vertices = vertices ,
annotations = annotations ,
)
trapz = Polygon (
layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
repetition = repetition ,
vertices = vertices ,
annotations = annotations ,
)
pat . shapes . append ( trapz )
elif isinstance ( element , fatrec . CTrapezoid ) :
@ -412,21 +423,24 @@ def read(
vertices [ 0 , 1 ] + = width
annotations = properties_to_annotations ( element . properties , lib . propnames , lib . propstrings )
ctrapz = Polygon ( layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
repetition = repetition ,
vertices = vertices ,
annotations = annotations ,
)
ctrapz = Polygon (
layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
repetition = repetition ,
vertices = vertices ,
annotations = annotations ,
)
pat . shapes . append ( ctrapz )
elif isinstance ( element , fatrec . Circle ) :
annotations = properties_to_annotations ( element . properties , lib . propnames , lib . propstrings )
circle = Circle ( layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
repetition = repetition ,
annotations = annotations ,
radius = float ( element . get_radius ( ) ) )
circle = Circle (
layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
repetition = repetition ,
annotations = annotations ,
radius = float ( element . get_radius ( ) ) ,
)
pat . shapes . append ( circle )
elif isinstance ( element , fatrec . Text ) :
@ -436,11 +450,13 @@ def read(
string = lib . textstrings [ str_or_ref ] . string
else :
string = str_or_ref . string
label = Label ( layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
repetition = repetition ,
annotations = annotations ,
string = string )
label = Label (
layer = element . get_layer_tuple ( ) ,
offset = element . get_xy ( ) ,
repetition = repetition ,
annotations = annotations ,
string = string ,
)
pat . labels . append ( label )
else :
@ -499,14 +515,16 @@ def _placement_to_subpat(placement: fatrec.Placement, lib: fatamorgana.OasisLayo
rotation = 0
else :
rotation = numpy . deg2rad ( float ( placement . angle ) )
subpat = SubPattern ( offset = xy ,
pattern = None ,
mirrored = ( placement . flip , False ) ,
rotation = rotation ,
scale = float ( mag ) ,
identifier = ( name , ) ,
repetition = repetition_fata2masq ( placement . repetition ) ,
annotations = annotations )
subpat = SubPattern (
offset = xy ,
pattern = None ,
mirrored = ( placement . flip , False ) ,
rotation = rotation ,
scale = float ( mag ) ,
identifier = ( name , ) ,
repetition = repetition_fata2masq ( placement . repetition ) ,
annotations = annotations ,
)
return subpat
@ -522,7 +540,7 @@ def _subpatterns_to_placements(
mirror_across_x , extra_angle = normalize_mirror ( subpat . mirrored )
frep , rep_offset = repetition_masq2fata ( subpat . repetition )
offset = numpy. round ( subpat . offset + rep_offset ) . astype ( int )
offset = rint_cast ( subpat . offset + rep_offset )
angle = numpy . rad2deg ( subpat . rotation + extra_angle ) % 360
ref = fatrec . Placement (
name = subpat . pattern . name ,
@ -532,7 +550,8 @@ def _subpatterns_to_placements(
properties = annotations_to_properties ( subpat . annotations ) ,
x = offset [ 0 ] ,
y = offset [ 1 ] ,
repetition = frep )
repetition = frep ,
)
refs . append ( ref )
return refs
@ -549,46 +568,51 @@ def _shapes_to_elements(
repetition , rep_offset = repetition_masq2fata ( shape . repetition )
properties = annotations_to_properties ( shape . annotations )
if isinstance ( shape , Circle ) :
offset = numpy . round ( shape . offset + rep_offset ) . astype ( int )
radius = numpy . round ( shape . radius ) . astype ( int )
circle = fatrec . Circle ( layer = layer ,
datatype = datatype ,
radius = radius ,
x = offset [ 0 ] ,
y = offset [ 1 ] ,
properties = properties ,
repetition = repetition )
offset = rint_cast ( shape . offset + rep_offset )
radius = rint_cast ( shape . radius )
circle = fatrec . Circle (
layer = layer ,
datatype = datatype ,
radius = radius ,
x = offset [ 0 ] ,
y = offset [ 1 ] ,
properties = properties ,
repetition = repetition ,
)
elements . append ( circle )
elif isinstance ( shape , Path ) :
xy = numpy. round ( shape . offset + shape . vertices [ 0 ] + rep_offset ) . astype ( int )
deltas = numpy. round ( numpy . diff ( shape . vertices , axis = 0 ) ) . astype ( int )
half_width = numpy. round ( shape . width / 2 ) . astype ( int )
xy = rint_cast ( shape . offset + shape . vertices [ 0 ] + rep_offset )
deltas = rint_cast ( numpy . diff ( shape . vertices , axis = 0 ) )
half_width = rint_cast ( shape . width / 2 )
path_type = next ( k for k , v in path_cap_map . items ( ) if v == shape . cap ) # reverse lookup
extension_start = ( path_type , shape . cap_extensions [ 0 ] if shape . cap_extensions is not None else None )
extension_end = ( path_type , shape . cap_extensions [ 1 ] if shape . cap_extensions is not None else None )
path = fatrec . Path ( layer = layer ,
datatype = datatype ,
point_list = deltas ,
half_width = half_width ,
x = xy [ 0 ] ,
y = xy [ 1 ] ,
extension_start = extension_start , # TODO implement multiple cap types?
extension_end = extension_end ,
properties = properties ,
repetition = repetition ,
)
path = fatrec . Path (
layer = layer ,
datatype = datatype ,
point_list = deltas ,
half_width = half_width ,
x = xy [ 0 ] ,
y = xy [ 1 ] ,
extension_start = extension_start , # TODO implement multiple cap types?
extension_end = extension_end ,
properties = properties ,
repetition = repetition ,
)
elements . append ( path )
else :
for polygon in shape . to_polygons ( ) :
xy = numpy . round ( polygon . offset + polygon . vertices [ 0 ] + rep_offset ) . astype ( int )
points = numpy . round ( numpy . diff ( polygon . vertices , axis = 0 ) ) . astype ( int )
elements . append ( fatrec . Polygon ( layer = layer ,
datatype = datatype ,
x = xy [ 0 ] ,
y = xy [ 1 ] ,
point_list = points ,
properties = properties ,
repetition = repetition ) )
xy = rint_cast ( polygon . offset + polygon . vertices [ 0 ] + rep_offset )
points = rint_cast ( numpy . diff ( polygon . vertices , axis = 0 ) )
elements . append ( fatrec . Polygon (
layer = layer ,
datatype = datatype ,
x = xy [ 0 ] ,
y = xy [ 1 ] ,
point_list = points ,
properties = properties ,
repetition = repetition ,
) )
return elements
@ -600,15 +624,17 @@ def _labels_to_texts(
for label in labels :
layer , datatype = layer2oas ( label . layer )
repetition , rep_offset = repetition_masq2fata ( label . repetition )
xy = numpy. round ( label . offset + rep_offset ) . astype ( int )
xy = rint_cast ( label . offset + rep_offset )
properties = annotations_to_properties ( label . annotations )
texts . append ( fatrec . Text ( layer = layer ,
datatype = datatype ,
x = xy [ 0 ] ,
y = xy [ 1 ] ,
string = label . string ,
properties = properties ,
repetition = repetition ) )
texts . append ( fatrec . Text (
layer = layer ,
datatype = datatype ,
x = xy [ 0 ] ,
y = xy [ 1 ] ,
string = label . string ,
properties = properties ,
repetition = repetition ,
) )
return texts
@ -648,10 +674,12 @@ def repetition_fata2masq(
) - > Optional [ Repetition ] :
mrep : Optional [ Repetition ]
if isinstance ( rep , fatamorgana . GridRepetition ) :
mrep = Grid ( a_vector = rep . a_vector ,
b_vector = rep . b_vector ,
a_count = rep . a_count ,
b_count = rep . b_count )
mrep = Grid (
a_vector = rep . a_vector ,
b_vector = rep . b_vector ,
a_count = rep . a_count ,
b_count = rep . b_count ,
)
elif isinstance ( rep , fatamorgana . ArbitraryRepetition ) :
displacements = numpy . cumsum ( numpy . column_stack ( ( rep . x_displacements ,
rep . y_displacements ) ) , axis = 0 )
@ -670,15 +698,20 @@ def repetition_masq2fata(
Tuple [ int , int ] ] :
frep : Union [ fatamorgana . GridRepetition , fatamorgana . ArbitraryRepetition , None ]
if isinstance ( rep , Grid ) :
a_vector = rint_cast ( rep . a_vector )
b_vector = rint_cast ( rep . b_vector ) if rep . b_vector is not None else None
a_count = rint_cast ( rep . a_count )
b_count = rint_cast ( rep . b_count ) if rep . b_count is not None else None
frep = fatamorgana . GridRepetition (
a_vector = numpy . round ( rep . a_vector ) . astype ( int ) ,
b_vector = numpy . round ( rep . b_vector ) . astype ( int ) ,
a_count = numpy . round ( rep . a_count ) . astype ( int ) ,
b_count = numpy . round ( rep . b_count ) . astype ( int ) )
a_vector = a_vector ,
b_vector = b_vector ,
a_count = a_count ,
b_count = b_count ,
)
offset = ( 0 , 0 )
elif isinstance ( rep , Arbitrary ) :
diffs = numpy . diff ( rep . displacements , axis = 0 )
diff_ints = numpy. round ( diffs ) . astype ( int )
diff_ints = rint_cast( diffs )
frep = fatamorgana . ArbitraryRepetition ( diff_ints [ : , 0 ] , diff_ints [ : , 1 ] )
offset = rep . displacements [ 0 , : ]
else :