2024-04-17 15:45:37 +02:00
from argparse import ArgumentParser
2024-04-15 20:40:15 +02:00
from csv import reader as csv_reader
2024-04-17 15:33:10 +02:00
from pprint import pformat
from xml . dom . minidom import parseString as xml_parser
2024-04-17 21:23:26 +02:00
from xml . etree . ElementTree import Element as XMLElement
from xml . etree . ElementTree import SubElement as XMLSubElement
from xml . etree . ElementTree import tostring as element_stringifier
2024-04-15 20:40:15 +02:00
class System :
""" Represents an L-System """
def __init__ (
self ,
name : str ,
2024-04-17 15:37:25 +02:00
base : list [ str ] ,
2024-04-15 20:40:15 +02:00
axiom : str ,
substitutions : list [ tuple [ str , str ] ] ,
interpretations : list [ tuple [ str , str ] ] ,
) :
2024-05-06 17:20:17 +02:00
# Space trimming for class members
2024-05-06 15:58:40 +02:00
self . name = name . strip ( )
2024-04-15 20:40:15 +02:00
""" System name """
2024-05-06 15:58:40 +02:00
self . base = map ( str . strip , base )
2024-04-15 20:40:15 +02:00
""" Set used """
2024-05-06 15:58:40 +02:00
self . axiom = axiom . strip ( )
2024-04-15 20:40:15 +02:00
""" Axiom used at the start of iterations """
2024-05-06 15:58:40 +02:00
self . substitutions = [ ( m . strip ( ) , s . strip ( ) ) for m , s in substitutions ]
2024-04-15 20:40:15 +02:00
"""
Substitution for each member of the base ,
represented as a couple ( member , substitution )
"""
2024-05-06 15:58:40 +02:00
self . interpretations = [ ( m . strip ( ) , i . strip ( ) ) for m , i in interpretations ]
2024-04-15 20:40:15 +02:00
"""
Interpretation for each member of the base ,
represented as a couple ( member , interpretation )
"""
def __repr__ ( self ) :
2024-04-26 21:22:36 +02:00
return pformat ( object = self . __dict__ , compact = True , width = 120 , sort_dicts = False )
2024-04-15 20:40:15 +02:00
2024-04-17 21:23:26 +02:00
def to_xml ( self ) - > XMLElement :
2024-04-17 15:33:10 +02:00
""" Convert the current system into an XML element """
2024-04-17 21:23:26 +02:00
system = XMLElement ( " lsystem " )
2024-04-17 15:33:10 +02:00
2024-04-17 21:23:26 +02:00
base = XMLSubElement ( system , " name " )
2024-04-17 21:18:36 +02:00
base . text = self . name
2024-04-17 21:23:26 +02:00
base = XMLSubElement ( system , " base " )
2024-04-17 15:33:10 +02:00
base . text = " " . join ( self . base )
2024-04-17 21:23:26 +02:00
axiom = XMLSubElement ( system , " axiom " )
2024-04-17 15:33:10 +02:00
axiom . text = self . axiom
2024-04-17 21:23:26 +02:00
substitutions = XMLSubElement ( system , " substitutions " )
2024-04-17 15:33:10 +02:00
for member , substitution in self . substitutions :
2024-04-17 21:23:26 +02:00
sub_element = XMLSubElement ( substitutions , " substitution " )
2024-04-17 15:33:10 +02:00
sub_element . set ( " member " , member )
sub_element . text = substitution
2024-04-17 21:23:26 +02:00
interpretations = XMLSubElement ( system , " interpretations " )
2024-04-17 15:33:10 +02:00
for member , interpretation in self . interpretations :
2024-04-17 21:23:26 +02:00
inter_element = XMLSubElement ( interpretations , " interpretation " )
2024-04-17 15:33:10 +02:00
inter_element . set ( " member " , member )
2024-05-06 17:20:17 +02:00
# If the interpretation is "do nothing", then do nothing
if len ( interpretation ) == 0 :
interpretation = " TURN 0 "
2024-04-17 15:33:10 +02:00
inter_element . text = interpretation
return system
2024-04-15 20:40:15 +02:00
def data_reader ( path : str , delimiter : str = " , " ) :
""" Read a CSV file and returns a list of L-System """
res : list [ System ] = [ ]
with open ( path ) as csv_file :
data = csv_reader ( csv_file , delimiter = delimiter )
for system in data :
2024-05-06 15:43:00 +02:00
if len ( system ) == 0 :
continue
2024-04-15 20:40:15 +02:00
name = system [ 0 ]
2024-04-17 15:37:25 +02:00
base = list ( system [ 1 ] )
2024-04-15 20:40:15 +02:00
axiom = system [ 2 ]
substitutions = [ ( v , system [ 3 + i ] ) for i , v in enumerate ( base ) ]
interpretations = [
( v , system [ 3 + i + len ( substitutions ) ] ) for i , v in enumerate ( base )
]
res . append ( System ( name , base , axiom , substitutions , interpretations ) )
2024-04-17 15:37:25 +02:00
2024-04-15 20:40:15 +02:00
return res
2024-04-17 21:23:26 +02:00
def lsystems_xml ( systems : list [ System ] ) - > XMLElement :
2024-04-17 15:33:10 +02:00
""" Convert list of L-system structure into XML """
2024-04-17 21:23:26 +02:00
root = XMLElement ( " lsystems " )
2024-04-17 15:33:10 +02:00
for system in systems :
root . append ( system . to_xml ( ) )
return root
2024-04-15 20:40:15 +02:00
if __name__ == " __main__ " :
2024-04-17 15:45:37 +02:00
parser = ArgumentParser ( description = " Generate XML representation of L-systems " )
parser . add_argument (
" file " , metavar = " FILE " , help = " CSV file containing L-system data "
)
parser . add_argument (
" -o " , " --output " , metavar = " OUTPUT_FILE " , help = " Specify output file for XML "
)
args = parser . parse_args ( )
2024-04-17 15:33:10 +02:00
# Read data
2024-04-17 15:45:37 +02:00
lsystems = data_reader ( args . file )
2024-04-15 20:40:15 +02:00
2024-04-17 15:33:10 +02:00
# Generate XML
xml = lsystems_xml ( lsystems )
2024-04-15 20:45:09 +02:00
2024-04-26 21:22:36 +02:00
# Add XML model declaration
2024-05-05 18:36:42 +02:00
xml_string = (
' <?xml-model href= " l-systems.xsd " ?> \n ' + element_stringifier ( xml ) . decode ( )
)
2024-04-26 21:22:36 +02:00
2024-04-17 15:45:37 +02:00
# Output XML
2024-04-26 21:22:36 +02:00
dom = xml_parser ( xml_string )
2024-05-06 14:43:07 +02:00
pretty_xml = dom . toprettyxml ( indent = " " , encoding = " UTF-8 " ) . decode ( )
2024-04-17 15:45:37 +02:00
if args . output :
with open ( args . output , " w " ) as output_file :
output_file . write ( pretty_xml )
else :
print ( pretty_xml )