cakefoot/src/extract_svg.py

80 lines
3.3 KiB
Python

# CAKEFOOT by @ohsqueezy for <https://foam.shampoo.ooo> and <https://ohsqueezy.itch.io>
#
# This file contains functions to facilitate transforming an SVG file with bezier curve data exported from GIMP into a Cakefoot level.
#
# This script is not essential for building the game. It is intended to be used manually to help build the `curve` field for a level in
# `resource/levels.json`.
#
# Create a path in GIMP, and export it to file by right-clicking the path in the path tool tab. Call the groups function on the path, and
# a list will be returned with one group of points per curve in the file.
import pathlib
import numpy as np
from bs4 import BeautifulSoup as BS
def groups(path: pathlib.Path, reverse: bool = False) -> list[list[tuple[float, float]]]:
"""
Read the groups of bezier curves in an SVG file exported by the GIMP path tool. A list of curves will be returned, each group in the
list consisting of a list of 2D floating point coordinates.
@param path SVG file with bezier curve
@param reverse For each group of points, return the reverse order they appear in the file
@return A three dimensional list, groups of 2D float coordinates
"""
groups = []
with path.open() as svg:
# Parse using XML mode, extract the "d" attribute from the "path" element
for content in BS(svg, features="xml").find("path").attrs["d"].split():
# Unless the content is just "M" or "C", it should contain a coordinate
if content != "M" and content != "C":
group = content
# Trim off the M that indicates the next group start at the end of the content
if content.endswith("M"):
group = content[:-1]
# Parse the coordinate data
groups[-1].append(tuple(float(point) for point in group.split(",")))
# A single "M" or a coordinate ending in "M" indicates a new group is starting
if content.endswith("M"):
groups.append([])
# Reverse groups after finished parsing if requested
if reverse:
for ii in range(len(groups)):
groups[ii] = list(reversed(groups[ii]))
return groups
def wrap_group(group: list[tuple[float, float]], x_offset: float, y_offset: float) -> list[tuple[float, float]]:
"""
Offset a given a list of 2D points by the given offset.
@param group List of 2D points
@param x_offset Amount to move in the X direction
@param y_offset Amount to move in the Y direction
@return Group after applying offset
"""
offset_group = []
for coordinates in group:
offset_group.append((coordinates[0] + x_offset, coordinates[1] + y_offset))
return offset_group
def format_group(group: list[tuple[float, float]]) -> str:
"""
@param group A 2D bezier curve as a list of float coordinates (see `groups` function)
@return Curve printed as a string appropriate for a JSON array for use with the Cakefoot levels file
"""
output = "\"curve\": ["
for ii, coordinates in enumerate(group):
if ii % 6 == 0:
output += "\n "
output += f"[{coordinates[0]:8}, {coordinates[1]:8}]"
if ii < len(group) - 1:
output += ", "
output += "\n]"
return output