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

140 lines
4.2 KiB
Python
Raw Permalink Normal View History

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)