This repository has been archived on 2024-05-19. You can view files and clone it, but cannot push or open issues or pull requests.
l-systems/converter.py
2024-05-06 15:43:00 +02:00

144 lines
4.3 KiB
Python

from argparse import ArgumentParser
from csv import reader as csv_reader
from pprint import pformat
from xml.dom.minidom import parseString as xml_parser
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
class System:
"""Represents an L-System"""
def __init__(
self,
name: str,
base: list[str],
axiom: str,
substitutions: list[tuple[str, str]],
interpretations: list[tuple[str, str]],
):
self.name = name
"""System name"""
self.base = base
"""Set used"""
self.axiom = axiom
"""Axiom used at the start of iterations"""
self.substitutions = substitutions
"""
Substitution for each member of the base,
represented as a couple (member, substitution)
"""
self.interpretations = interpretations
"""
Interpretation for each member of the base,
represented as a couple (member, interpretation)
"""
# Interpretation of extra symbols added if necessary
extra_symbols = ["[", "]"]
for _, substitution in substitutions:
if any(symbol in substitution for symbol in extra_symbols):
self.interpretations.extend(
[(extra_symbols[0], "STORE"), (extra_symbols[1], "RESTORE")]
)
break
def __repr__(self):
return pformat(object=self.__dict__, compact=True, width=120, sort_dicts=False)
def to_xml(self) -> XMLElement:
"""Convert the current system into an XML element"""
system = XMLElement("lsystem")
base = XMLSubElement(system, "name")
base.text = self.name
base = XMLSubElement(system, "base")
base.text = "".join(self.base)
axiom = XMLSubElement(system, "axiom")
axiom.text = self.axiom
substitutions = XMLSubElement(system, "substitutions")
for member, substitution in self.substitutions:
sub_element = XMLSubElement(substitutions, "substitution")
sub_element.set("member", member)
sub_element.text = substitution
interpretations = XMLSubElement(system, "interpretations")
for member, interpretation in self.interpretations:
inter_element = XMLSubElement(interpretations, "interpretation")
inter_element.set("member", member)
inter_element.text = interpretation
return system
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:
if len(system) == 0:
continue
name = system[0]
base = list(system[1])
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))
return res
def lsystems_xml(systems: list[System]) -> XMLElement:
"""Convert list of L-system structure into XML"""
root = XMLElement("lsystems")
for system in systems:
root.append(system.to_xml())
return root
if __name__ == "__main__":
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()
# Read data
lsystems = data_reader(args.file)
# Generate XML
xml = lsystems_xml(lsystems)
# Add XML model declaration
xml_string = (
'<?xml-model href="l-systems.xsd"?>\n' + element_stringifier(xml).decode()
)
# Output XML
dom = xml_parser(xml_string)
pretty_xml = dom.toprettyxml(indent=" ", encoding="UTF-8").decode()
if args.output:
with open(args.output, "w") as output_file:
output_file.write(pretty_xml)
else:
print(pretty_xml)