mirror of
https://github.com/enzet/map-machine.git
synced 2025-04-29 18:27:19 +02:00
Closes #17: use svgwrite.
This commit is contained in:
parent
715ffcacb1
commit
f3a9e149b4
6 changed files with 118 additions and 384 deletions
|
@ -1,2 +1,4 @@
|
|||
svgwrite
|
||||
numpy>=1.18.1
|
||||
PyYAML>=4.2b1
|
||||
urllib3>=1.25.6
|
||||
|
|
|
@ -4,22 +4,21 @@ Author: Sergey Vartanov (me@enzet.ru).
|
|||
|
||||
import os
|
||||
import random
|
||||
import svgwrite
|
||||
import yaml
|
||||
|
||||
from roentgen import extract_icon
|
||||
from roentgen import svg
|
||||
|
||||
from typing import Any, Dict
|
||||
|
||||
|
||||
def draw_icon(
|
||||
output_file, icon: Dict[str, Any], x: float, y: float,
|
||||
svg, icon: Dict[str, Any], x: float, y: float,
|
||||
color: str = "444444"):
|
||||
|
||||
output_file.write(
|
||||
f'<path d="{icon["path"]}" '
|
||||
f'style="fill:#{color};stroke:none;" '
|
||||
f'transform="translate({icon["x"] + x},{icon["y"] + y})" />\n')
|
||||
svg.add(svg.path(
|
||||
d=icon["path"], fill=f"#{color}", stroke="none",
|
||||
transform=f'translate({icon["x"] + x},{icon["y"] + y})'))
|
||||
|
||||
|
||||
def draw_grid():
|
||||
|
@ -103,16 +102,16 @@ def draw_grid():
|
|||
|
||||
height = int(number / (width / step) + 1) * step
|
||||
|
||||
output_file = svg.SVG(open(icon_grid_file_name, "w+"))
|
||||
output_file.begin(width, height)
|
||||
svg = svgwrite.Drawing(icon_grid_file_name, (width, height))
|
||||
|
||||
output_file.rect(0, 0, width, height, color="FFFFFF")
|
||||
svg.add(svg.rect((0, 0), (width, height), fill="#FFFFFF"))
|
||||
|
||||
for icon in icons:
|
||||
background_color, foreground_color = random.choice(icon_colors)
|
||||
output_file.rect(x - 2 - 8, y - 2 - 8, 20, 20, color=background_color)
|
||||
svg.add(svg.rect(
|
||||
(x - 2 - 8, y - 2 - 8), (20, 20), fill=f"#{background_color}"))
|
||||
for i in icon["icons"]:
|
||||
draw_icon(output_file, i, x, y, foreground_color)
|
||||
draw_icon(svg, i, x, y, foreground_color)
|
||||
x += step
|
||||
if x > width - 8:
|
||||
x = step / 2
|
||||
|
@ -121,4 +120,4 @@ def draw_grid():
|
|||
|
||||
print(f"Icons: {number}.")
|
||||
|
||||
output_file.end()
|
||||
svg.write(open(icon_grid_file_name, "w"))
|
||||
|
|
|
@ -3,23 +3,25 @@ Simple OpenStreetMap renderer.
|
|||
|
||||
Author: Sergey Vartanov (me@enzet.ru).
|
||||
"""
|
||||
import numpy as np
|
||||
import os
|
||||
import random
|
||||
import svgwrite
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
import numpy as np
|
||||
from svgwrite.container import Group
|
||||
from svgwrite.path import Path
|
||||
from svgwrite.shapes import Circle, Rect
|
||||
from svgwrite.text import Text
|
||||
from typing import List
|
||||
|
||||
from roentgen import extract_icon
|
||||
from roentgen import ui
|
||||
from roentgen import svg
|
||||
from roentgen.constructor import Constructor, get_path
|
||||
from roentgen.flinger import GeoFlinger, Geo
|
||||
from roentgen.osm_reader import OSMReader
|
||||
from roentgen.osm_getter import get_osm
|
||||
from roentgen.grid import draw_grid
|
||||
|
||||
from typing import List
|
||||
from roentgen.osm_getter import get_osm
|
||||
from roentgen.osm_reader import Map, OSMReader
|
||||
|
||||
ICONS_FILE_NAME: str = "icons/icons.svg"
|
||||
TAGS_FILE_NAME: str = "data/tags.yml"
|
||||
|
@ -31,7 +33,7 @@ class Painter:
|
|||
|
||||
def __init__(
|
||||
self, show_missing_tags, overlap, draw_nodes, mode, draw_captions,
|
||||
map_, flinger, output_file, icons, scheme):
|
||||
map_, flinger, svg: svgwrite.Drawing, icons, scheme):
|
||||
|
||||
self.show_missing_tags = show_missing_tags
|
||||
self.overlap = overlap
|
||||
|
@ -41,16 +43,10 @@ class Painter:
|
|||
|
||||
self.map_ = map_
|
||||
self.flinger = flinger
|
||||
self.output_file = output_file
|
||||
self.svg: svgwrite.Drawing = svg
|
||||
self.icons = icons
|
||||
self.scheme = scheme
|
||||
|
||||
def draw_raw_nodes(self):
|
||||
for node_id in self.map_.node_map:
|
||||
node = self.map_.node_map[node_id]
|
||||
flung = self.flinger.fling(node)
|
||||
self.output_file.circle(flung[0], flung[1], 0.2, color="FFFFFF")
|
||||
|
||||
def no_draw(self, key):
|
||||
if key in self.scheme["tags_to_write"] or \
|
||||
key in self.scheme["tags_to_skip"]:
|
||||
|
@ -125,27 +121,21 @@ class Painter:
|
|||
#------#
|
||||
######
|
||||
"""
|
||||
text = text.replace("&", "and")
|
||||
if out_fill_2:
|
||||
self.output_file.write(
|
||||
f'<text x="{x}" y="{y}" style="font-size:' +
|
||||
str(
|
||||
size) + ";text-anchor:middle;font-family:Roboto;fill:#" +
|
||||
out_fill_2 + ";stroke-linejoin:round;stroke-width:5;stroke:#" +
|
||||
out_fill_2 + ";opacity:" + str(
|
||||
out_opacity_2) + ';">' + text + "</text>")
|
||||
self.svg.add(Text(
|
||||
text, (x, y), font_size=size, text_anchor="middle",
|
||||
font_family="Roboto", fill=f"#{out_fill_2}",
|
||||
stroke_linejoin="round", stroke_width=5,
|
||||
stroke=f"#{out_fill_2}", opacity=out_opacity_2))
|
||||
if out_fill:
|
||||
self.output_file.write(
|
||||
f'<text x="{x}" y="{y}" style="font-size:' +
|
||||
str(
|
||||
size) + ";text-anchor:middle;font-family:Roboto;fill:#" +
|
||||
out_fill + ";stroke-linejoin:round;stroke-width:3;stroke:#" +
|
||||
out_fill + ";opacity:" + str(
|
||||
out_opacity) + ';">' + text + "</text>")
|
||||
self.output_file.write(
|
||||
f'<text x="{x}" y="{y}" style="font-size:' +
|
||||
str(size) + ";text-anchor:middle;font-family:Roboto;fill:#" +
|
||||
fill + ';">' + text + "</text>")
|
||||
self.svg.add(Text(
|
||||
text, (x, y), font_size=size, text_anchor="middle",
|
||||
font_family="Roboto", fill=f"#{out_fill}",
|
||||
stroke_linejoin="round", stroke_width=3,
|
||||
stroke=f"#{out_fill}", opacity=out_opacity))
|
||||
self.svg.add(Text(
|
||||
text, (x, y), font_size=size, text_anchor="middle",
|
||||
font_family="Roboto", fill=f"#{fill}"))
|
||||
|
||||
def wr(self, text, x, y, fill, text_y, size=10):
|
||||
text = text[:26] + ("..." if len(text) > 26 else "")
|
||||
|
@ -268,16 +258,12 @@ class Painter:
|
|||
node_2 = self.map_.node_map[way.nodes[i + 1]]
|
||||
flung_1 = self.flinger.fling(Geo(node_1.lat, node_1.lon))
|
||||
flung_2 = self.flinger.fling(Geo(node_2.lat, node_2.lon))
|
||||
shifted_1 = np.add(flung_1, shift_1)
|
||||
shifted_2 = np.add(flung_2, shift_2)
|
||||
self.output_file.write(
|
||||
f'<path d="M '
|
||||
f"{shifted_1[0]},{shifted_1[1]} L "
|
||||
f"{shifted_1[0]},{shifted_1[1]} "
|
||||
f"{shifted_2[0]},{shifted_2[1]} "
|
||||
f'{shifted_2[0]},{shifted_2[1]} Z" '
|
||||
f'style="fill:#{color};stroke:#{color};'
|
||||
f'stroke-width:1;" />\n')
|
||||
|
||||
self.svg.add(self.svg.path(
|
||||
d=("M", np.add(flung_1, shift_1), "L",
|
||||
np.add(flung_2, shift_1), np.add(flung_2, shift_2),
|
||||
np.add(flung_1, shift_2), "Z"),
|
||||
fill=f"#{color}", stroke=f"#{color}", stroke_width=1))
|
||||
elif way.path:
|
||||
# TODO: implement
|
||||
pass
|
||||
|
@ -289,18 +275,17 @@ class Painter:
|
|||
if way.kind == "way":
|
||||
if way.nodes:
|
||||
path = get_path(way.nodes, [0, 0], self.map_, self.flinger)
|
||||
self.output_file.write(
|
||||
f'<path d="{path}" style="' + way.style + '" />\n')
|
||||
self.svg.add(Path(d=path, style=way.style))
|
||||
else:
|
||||
self.output_file.write(
|
||||
f'<path d="{way.path}" style="' + way.style + '" />\n')
|
||||
self.svg.add(Path(d=way.path, style=way.style))
|
||||
|
||||
# Building shade
|
||||
|
||||
self.output_file.write('<g style="opacity:0.1;">\n')
|
||||
building_shade = Group(opacity=0.1)
|
||||
|
||||
for way in ways:
|
||||
if way.kind == "building":
|
||||
if way.nodes:
|
||||
if way.kind != "building" or not way.nodes:
|
||||
continue
|
||||
shift = [-5, 5]
|
||||
if way.levels:
|
||||
shift = [-5 * way.levels, 5 * way.levels]
|
||||
|
@ -309,14 +294,12 @@ class Painter:
|
|||
node_2 = self.map_.node_map[way.nodes[i + 1]]
|
||||
flung_1 = self.flinger.fling(Geo(node_1.lat, node_1.lon))
|
||||
flung_2 = self.flinger.fling(Geo(node_2.lat, node_2.lon))
|
||||
self.output_file.write(
|
||||
f'<path d="M '
|
||||
f'{flung_1[0]},{flung_1[1]} L '
|
||||
f"{flung_2[0]},{flung_2[1]} "
|
||||
f"{flung_2[0] + shift[0]},{flung_2[1] + shift[1]} "
|
||||
f'{flung_1[0] + shift[0]},{flung_1[1] + shift[1]} Z" '
|
||||
f'style="fill:#000000;stroke:#000000;stroke-width:1;" />\n')
|
||||
self.output_file.write("</g>\n")
|
||||
building_shade.add(Path(
|
||||
("M", flung_1, "L", flung_2, np.add(flung_2, shift),
|
||||
np.add(flung_1, shift), "Z"),
|
||||
fill="#000000", stroke="#000000", stroke_width=1))
|
||||
|
||||
self.svg.add(building_shade)
|
||||
|
||||
# Building walls
|
||||
|
||||
|
@ -327,18 +310,16 @@ class Painter:
|
|||
# Building roof
|
||||
|
||||
for way in ways:
|
||||
if way.kind == "building":
|
||||
if way.kind != "building":
|
||||
continue
|
||||
if way.nodes:
|
||||
shift = [0, -3]
|
||||
if way.levels:
|
||||
shift = [0 * way.levels, min(-3, -1 * way.levels)]
|
||||
path = get_path(way.nodes, shift, self.map_, self.flinger)
|
||||
self.output_file.write(
|
||||
f'<path d="{path}" style="{way.style};opacity:1;" />\n')
|
||||
self.svg.add(Path(d=path, style=way.style, opacity=1))
|
||||
else:
|
||||
self.output_file.write(
|
||||
f'<path d="{way.path}" '
|
||||
f'style="{way.style};opacity:1;" />\n')
|
||||
self.svg.add(Path(d=way.path, style=way.style, opacity=1))
|
||||
|
||||
# Trees
|
||||
|
||||
|
@ -347,11 +328,10 @@ class Painter:
|
|||
node.tags["natural"] == "tree" and
|
||||
"diameter_crown" in node.tags):
|
||||
continue
|
||||
self.output_file.circle(
|
||||
float(node.x) + (random.random() - 0.5) * 10,
|
||||
float(node.y) + (random.random() - 0.5) * 10,
|
||||
self.svg.add(Circle(
|
||||
(float(node.x), float(node.y)),
|
||||
float(node.tags["diameter_crown"]) * 1.2,
|
||||
color='688C44', fill='688C44', opacity=0.3)
|
||||
fill="#688C44", stroke="#688C44", opacity=0.3))
|
||||
|
||||
# All other nodes
|
||||
|
||||
|
@ -386,20 +366,15 @@ class Painter:
|
|||
def draw_point(self, shape, x, y, fill, size=16, xx=0, yy=0, tags=None):
|
||||
x = int(float(x))
|
||||
y = int(float(y))
|
||||
self.output_file.write(
|
||||
'<path d="' + shape + '" style="fill:#' + fill +
|
||||
';fill-opacity:1" transform="translate(' +
|
||||
str(x - size / 2.0 - xx * 16) + "," + str(
|
||||
y - size / 2.0 - yy * 16) +
|
||||
')">')
|
||||
if tags:
|
||||
self.output_file.write("<title>")
|
||||
self.output_file.write(
|
||||
"\n".join(map(lambda x: x + ": " + tags[x], tags)))
|
||||
self.output_file.write("</title>")
|
||||
self.output_file.write("</path>\n")
|
||||
path = self.svg.path(
|
||||
d=shape, fill=f"#{fill}", fill_opacity=1,
|
||||
transform=f"translate({x - size / 2.0 - xx * 16},"
|
||||
f"{y - size / 2.0 - yy * 16})")
|
||||
path.set_desc(title="\n".join(map(lambda x: x + ": " + tags[x], tags)))
|
||||
self.svg.add(path)
|
||||
|
||||
def draw_point_outline(self, shape, x, y, fill, mode="default", size=16, xx=0, yy=0):
|
||||
def draw_point_outline(
|
||||
self, shape, x, y, fill, mode="default", size=16, xx=0, yy=0):
|
||||
x = int(float(x))
|
||||
y = int(float(y))
|
||||
opacity = 0.5
|
||||
|
@ -413,12 +388,12 @@ class Painter:
|
|||
if Y > 200:
|
||||
outline_fill = "000000"
|
||||
opacity = 0.7
|
||||
self.output_file.write(
|
||||
'<path d="' + shape + '" style="fill:#' + outline_fill + ";opacity:" +
|
||||
str(opacity) + ";" + "stroke:#" + outline_fill +
|
||||
f';stroke-width:{stroke_width};stroke-linejoin:round;" ' + 'transform="translate(' +
|
||||
str(x - size / 2.0 - xx * 16) + "," + str(y - size / 2.0 - yy * 16) +
|
||||
')" />\n')
|
||||
self.svg.add(self.svg.path(
|
||||
d=shape, fill=f"#{outline_fill}", opacity=opacity,
|
||||
stroke=f"#{outline_fill}", stroke_width=stroke_width,
|
||||
stroke_linejoin="round",
|
||||
transform=f"translate({x - size / 2.0 - xx * 16},"
|
||||
f"{y - size / 2.0 - yy * 16})"))
|
||||
|
||||
|
||||
def check_level_number(tags, level):
|
||||
|
@ -453,9 +428,9 @@ def main():
|
|||
if not options:
|
||||
sys.exit(1)
|
||||
|
||||
background_color = "EEEEEE"
|
||||
background_color = "#EEEEEE"
|
||||
if options.mode in ["user-coloring", "time"]:
|
||||
background_color = "111111"
|
||||
background_color = "#111111"
|
||||
|
||||
if options.input_file_name:
|
||||
input_file_name = options.input_file_name
|
||||
|
@ -480,18 +455,18 @@ def main():
|
|||
print("Fatal: no such file: " + file_name + ".")
|
||||
sys.exit(1)
|
||||
|
||||
map_ = osm_reader.parse_osm_file(
|
||||
osm_reader.parse_osm_file(
|
||||
file_name, parse_ways=options.draw_ways,
|
||||
parse_relations=options.draw_ways, full=full)
|
||||
|
||||
output_file = svg.SVG(open(options.output_file_name, "w+"))
|
||||
map_: Map = osm_reader.map_
|
||||
|
||||
w, h = list(map(lambda x: float(x), options.size.split(",")))
|
||||
|
||||
output_file.begin(w, h)
|
||||
output_file.write(
|
||||
"<title>Rӧntgen</title><style>path:hover {stroke: #FF0000;}</style>\n")
|
||||
output_file.rect(0, 0, w, h, color=background_color)
|
||||
svg: svgwrite.Drawing = \
|
||||
svgwrite.Drawing(options.output_file_name, size=(w, h))
|
||||
|
||||
svg.add(Rect((0, 0), (w, h), fill=background_color))
|
||||
|
||||
min1 = Geo(boundary_box[1], boundary_box[0])
|
||||
max1 = Geo(boundary_box[3], boundary_box[2])
|
||||
|
@ -537,18 +512,18 @@ def main():
|
|||
show_missing_tags=options.show_missing_tags, overlap=options.overlap,
|
||||
draw_nodes=options.draw_nodes, mode=options.mode,
|
||||
draw_captions=options.draw_captions,
|
||||
map_=map_, flinger=flinger, output_file=output_file, icons=icons,
|
||||
map_=map_, flinger=flinger, svg=svg, icons=icons,
|
||||
scheme=scheme)
|
||||
painter.draw(constructor.nodes, constructor.ways, points)
|
||||
|
||||
if flinger.space[0] == 0:
|
||||
output_file.rect(0, 0, w, flinger.space[1], color="FFFFFF")
|
||||
output_file.rect(
|
||||
0, h - flinger.space[1], w, flinger.space[1], color="FFFFFF")
|
||||
svg.add(Rect((0, 0), (w, flinger.space[1]), fill="#FFFFFF"))
|
||||
svg.add(Rect(
|
||||
(0, h - flinger.space[1]), (w, flinger.space[1]), fill="#FFFFFF"))
|
||||
if flinger.space[1] == 0:
|
||||
output_file.rect(0, 0, flinger.space[0], h, color="FFFFFF")
|
||||
output_file.rect(
|
||||
w - flinger.space[0], 0, flinger.space[0], h, color="FFFFFF")
|
||||
svg.add(Rect((0, 0), (flinger.space[0], h), fill="#FFFFFF"))
|
||||
svg.add(Rect(
|
||||
(w - flinger.space[0], 0), (flinger.space[0], h), fill="#FFFFFF"))
|
||||
|
||||
if options.show_index:
|
||||
print(min1.lon, max1.lon)
|
||||
|
@ -595,12 +570,13 @@ def main():
|
|||
t2 = flinger.fling(Geo(
|
||||
min1.lat + (i + 1) * lat_step,
|
||||
min1.lon + (j + 1) * lon_step))
|
||||
output_file.text(
|
||||
((t1 + t2) * 0.5)[0], ((t1 + t2) * 0.5)[1] + 40,
|
||||
str(int(matrix[i][j])), size=80, color="440000",
|
||||
opacity=0.1, align="center")
|
||||
svg.add(Text(
|
||||
str(int(matrix[i][j])),
|
||||
(((t1 + t2) * 0.5)[0], ((t1 + t2) * 0.5)[1] + 40),
|
||||
font_size=80, fill="440000",
|
||||
opacity=0.1, align="center"))
|
||||
|
||||
output_file.end()
|
||||
svg.write(open(options.output_file_name, "w"))
|
||||
|
||||
top_missing_tags = \
|
||||
sorted(missing_tags.keys(), key=lambda x: -missing_tags[x])
|
||||
|
|
|
@ -3,7 +3,7 @@ Reading OpenStreetMap data from XML file.
|
|||
|
||||
Author: Sergey Vartanov
|
||||
"""
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from roentgen import ui
|
||||
|
||||
|
|
245
roentgen/svg.py
245
roentgen/svg.py
|
@ -1,245 +0,0 @@
|
|||
"""
|
||||
Very simple SVG library.
|
||||
|
||||
Author: Sergey Vartanov (me@enzet.ru)
|
||||
"""
|
||||
|
||||
import math
|
||||
|
||||
|
||||
class SVG:
|
||||
def __init__(self, file_):
|
||||
self.file = file_
|
||||
self.index = 0
|
||||
|
||||
def begin(self, width, height):
|
||||
self.file.write(
|
||||
'<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n\n')
|
||||
self.file.write(
|
||||
f'''<svg version="1.1" baseProfile="full"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:ev="http://www.w3.org/2001/xml-events"
|
||||
bgcolor="#e5e9e6" width="{width}" height="{height}">\n''')
|
||||
|
||||
def end(self):
|
||||
self.file.write('</svg>\n')
|
||||
|
||||
def path(
|
||||
self, path: str, color: str = "black", width: float = 1,
|
||||
fill: str = "none", end: str = "butt", id: str = None,
|
||||
color2: str = None, gx1: float = 0, gy1: float = 0, gx2: float = 0,
|
||||
gy2: float = 0, dash: str = None, dashoffset: str = None,
|
||||
opacity: float = 1, transform: str = None):
|
||||
|
||||
if color2:
|
||||
self.index += 1
|
||||
self.file.write(
|
||||
f'<defs><linearGradient id="grad{str(self.index)}" '
|
||||
f'x1="{str(gx1)}" y1="{str(gy1)}" '
|
||||
f'x2="{str(gx2)}" y2="{str(gy2)}" '
|
||||
f'gradientUnits="userSpaceOnUse">'
|
||||
f'<stop style="stop-color:{self.get_color(color)};'
|
||||
f'stop-opacity:1;" offset="0" />'
|
||||
f'<stop style="stop-color:{self.get_color(color2)};'
|
||||
f'stop-opacity:1;" offset="1" /></linearGradient></defs>\n')
|
||||
|
||||
self.file.write(' <path d = "' + path + '" ')
|
||||
|
||||
if id:
|
||||
self.file.write(f'id="{id}" ')
|
||||
if transform:
|
||||
self.file.write('transform="' + transform + '" ')
|
||||
|
||||
self.file.write('style = "')
|
||||
if not color2:
|
||||
self.file.write('stroke:' + self.get_color(color) + '; ')
|
||||
else:
|
||||
self.file.write('stroke:url(#grad' + str(self.index) + '); ')
|
||||
self.file.write('stroke-width:' + str(width) + '; ')
|
||||
self.file.write('fill:' + self.get_color(fill) + '; ')
|
||||
self.file.write('stroke-linecap:' + end + '; ')
|
||||
if opacity != 1:
|
||||
self.file.write('opacity:' + str(opacity) + '; ')
|
||||
if dash:
|
||||
self.file.write('stroke-dasharray:' + dash + '; ')
|
||||
if dashoffset:
|
||||
self.file.write('stroke-dashoffset:' + str(dashoffset) + '; ')
|
||||
self.file.write('" />\n')
|
||||
|
||||
def line(
|
||||
self, x1, y1, x2, y2, width=1, color='black', end='butt', id_=None,
|
||||
color2=None, gx1=None, gy1=None, gx2=None, gy2=None, dash=None,
|
||||
dashoffset=None, opacity=None):
|
||||
|
||||
if color2:
|
||||
if not gx1:
|
||||
gx1 = x1
|
||||
if not gy1:
|
||||
gy1 = y1
|
||||
if not gx2:
|
||||
gx2 = x2
|
||||
if not gy2:
|
||||
gy2 = y2
|
||||
self.index += 1
|
||||
self.file.write(
|
||||
f'<defs><linearGradient id="grad{str(self.index)}" '
|
||||
f'x1="{str(gx1)}" y1="{str(gy1)}" x2="{str(gx2)}" '
|
||||
f'y2="{str(gy2)}" gradientUnits="userSpaceOnUse">\n'
|
||||
f'<stop style="stop-color:#{str(color)};stop-opacity:1;" '
|
||||
f'offset="0" />'
|
||||
f'<stop style=\"stop-color:#{str(color2)};stop-opacity:1;" '
|
||||
f'offset="1" />'
|
||||
f'</linearGradient></defs>\n')
|
||||
self.file.write(
|
||||
f' <path d = "M {str(x1)},{str(y1)} {str(x2)},{str(y2)}" ')
|
||||
if id_:
|
||||
self.file.write(f'id="{id_}" ')
|
||||
self.file.write('style="')
|
||||
if not color2:
|
||||
self.file.write(f'stroke:{self.get_color(color)}; ')
|
||||
else:
|
||||
self.file.write(f'stroke:url(#grad{self.index}); ')
|
||||
self.file.write(f'stroke-width:{width}; ')
|
||||
self.file.write(f'stroke-linecap:{end}; ')
|
||||
if dash:
|
||||
self.file.write('stroke-dasharray:' + dash + '; ')
|
||||
if dashoffset:
|
||||
self.file.write('stroke-dashoffset:' + str(dashoffset) + '; ')
|
||||
if opacity:
|
||||
self.file.write('opacity: ' + str(opacity) + '; ')
|
||||
self.file.write('" />\n')
|
||||
|
||||
def rect(
|
||||
self, x, y, width, height, color='black', id=None, rx=0, ry=0,
|
||||
opacity=1.0, stroke_color='none', stroke_width=1.0):
|
||||
|
||||
self.file.write(' <rect x = "' + str(x) + '" y = "' + str(y) + '" rx = "' + str(rx) + '" ry = "' + str(ry) +
|
||||
'" ')
|
||||
self.file.write(' width = "' + str(width) + '" height = "' + str(height) + '" ')
|
||||
if id:
|
||||
self.file.write('id = "' + id + '" ')
|
||||
self.file.write('style = "')
|
||||
if opacity != 1:
|
||||
self.file.write('opacity:' + str(opacity) + '; ')
|
||||
self.file.write('fill:' + self.get_color(color) + '; ')
|
||||
self.file.write('stroke:' + self.get_color(stroke_color) + '; ')
|
||||
self.file.write('stroke-width:' + str(stroke_width) + '; ')
|
||||
self.file.write('" />\n')
|
||||
|
||||
def curve(self, x1, y1, x2, y2, x3, y3, x4, y4, id=None, width=1, color='black'):
|
||||
self.file.write(' <path d = "M ' + str(x1) + ',' + str(y1) + ' C ' + str(x2) + ',' + str(y2) + ' ')
|
||||
self.file.write(str(x3) + ',' + str(y3) + ' ' + str(x4) + ',' + str(y4) + '" ')
|
||||
self.file.write('style = "')
|
||||
self.file.write('stroke:' + self.get_color(color) + '; ')
|
||||
self.file.write('stroke-width:' + str(width) + '; ')
|
||||
self.file.write('fill: none; ')
|
||||
self.file.write('" />\n')
|
||||
|
||||
def rhombus(self, x, y, width, height, color='black', id=None):
|
||||
self.file.write(''' <path d = "M %5.1f %5.1f L %5.1f %5.1f L %5.1f %5.1f
|
||||
L %5.1f %5.1f L %5.1f %5.1f" ''' % (x, y - height, x + width, y, x, y + height, x - width, y, x,
|
||||
y - height))
|
||||
if id:
|
||||
self.file.write('id = "' + id + '" ')
|
||||
self.file.write('style = "')
|
||||
self.file.write('fill:' + self.get_color(color) + '; ')
|
||||
self.file.write('" />\n')
|
||||
|
||||
def circle(self, x, y, d, color='black', color2='white', fill='none',
|
||||
opacity=None, width=1, id_=None, gx=0, gy=0, gr=0, fx=0, fy=0):
|
||||
is_grad = gx != 0 or gy != 0 or gr != 0
|
||||
if is_grad:
|
||||
self.index += 1
|
||||
self.file.write(
|
||||
f'<defs><radialGradient id="grad{str(self.index)}" '
|
||||
f'cx="{gx}%" cy="{gy}%" r="{gr}%" fx="{fx}%" fy="{fy}%">'
|
||||
f'<stop offset="0%" style="stop-color:rgb(255,255,255);'
|
||||
f'stop-opacity:0" /><stop offset="100%" '
|
||||
f'style="stop-color:{self.get_color(color)};stop-opacity:1" />'
|
||||
f'</radialGradient></defs>\n')
|
||||
c = 0.577
|
||||
self.file.write(
|
||||
f' <path d = "'
|
||||
f'M {x:5.1f} {y + d:5.1f} '
|
||||
f'C {x - d * c:5.1f} {y + d:5.1f} {x - d:5.1f} {y + d * c:5.1f} '
|
||||
f'{x - d:5.1f} {y:5.1f} '
|
||||
f'C {x - d:5.1f} {y - d * c:5.1f} {x - d * c:5.1f} {y - d:5.1f} '
|
||||
f'{x:5.1f} {y - d:5.1f} '
|
||||
f'C {x + d * c:5.1f} {y - d:5.1f} {x + d:5.1f} {y - d * c:5.1f} '
|
||||
f'{x + d:5.1f} {y:5.1f} '
|
||||
f'C {x + d:5.1f} {y + d * c:5.1f} {x + d * c:5.1f} {y + d:5.1f} '
|
||||
f'{x:5.1f} {y + d:5.1f}" ')
|
||||
|
||||
if id_:
|
||||
self.file.write('id = "' + id_ + '" ')
|
||||
self.file.write('style = "')
|
||||
if is_grad:
|
||||
self.file.write('fill:url(#grad' + str(self.index) + '); ')
|
||||
else:
|
||||
self.file.write('fill:' + self.get_color(fill) + '; ')
|
||||
self.file.write('stroke-width:' + str(width) + '; ')
|
||||
self.file.write('stroke:' + self.get_color(color) + '; ')
|
||||
if opacity:
|
||||
self.file.write('opacity:' + str(opacity) + '; ')
|
||||
self.file.write('" />\n')
|
||||
|
||||
def text(self, x, y, text, font='Myriad Pro', size='10', align='left',
|
||||
color='black', id=None, weight=None, letter_spacing=None, angle=None,
|
||||
opacity=None):
|
||||
"""
|
||||
Drawing SVG <text> element.
|
||||
"""
|
||||
if angle is None:
|
||||
self.file.write(f' <text x="{str(x)}" y="{str(y)}" ')
|
||||
else:
|
||||
self.file.write(' <text x="0" y="0" ')
|
||||
self.file.write('font-size = "' + str(size) + '" ')
|
||||
if id:
|
||||
self.file.write('id = "' + id + '" ')
|
||||
if not (angle is None) and angle <= 0:
|
||||
align = 'end'
|
||||
if align == 'right':
|
||||
align = 'end'
|
||||
if align == 'center':
|
||||
align = 'middle'
|
||||
self.file.write('style = "')
|
||||
self.file.write('text-anchor:' + align + '; ')
|
||||
if opacity:
|
||||
self.file.write('opacity:' + str(opacity) + '; ')
|
||||
self.file.write('font-family: ' + font + '; ')
|
||||
self.file.write('fill: ' + self.get_color(color) + '; ')
|
||||
if weight == 'bold':
|
||||
self.file.write('font-weight:bold; ')
|
||||
if letter_spacing:
|
||||
self.file.write('letter-spacing:' + str(letter_spacing) + '; ')
|
||||
self.file.write('"')
|
||||
if not (angle is None):
|
||||
if math.sin(angle) > 0:
|
||||
trans = 'transform = "matrix(' + str(math.sin(angle)) + ',' + str(math.cos(angle)) + ',' + \
|
||||
str(-math.cos(angle)) + ',' + str(math.sin(angle)) + ',' + str(x) + ',' + str(y) + ')"'
|
||||
else:
|
||||
trans = 'transform = "matrix(' + str(math.sin(angle + math.pi)) + ',' + str(math.cos(angle + math.pi)) + ',' + \
|
||||
str(-math.cos(angle + math.pi)) + ',' + str(math.sin(angle + math.pi)) + ',' + str(x) + ',' + str(y) + ')"'
|
||||
self.file.write(' ' + trans)
|
||||
self.file.write('>')
|
||||
self.file.write(text)
|
||||
self.file.write('</text>\n')
|
||||
|
||||
@staticmethod
|
||||
def get_color(color):
|
||||
if color == 'none':
|
||||
return 'none'
|
||||
if color == 'black':
|
||||
return 'black'
|
||||
return '#' + str(color)
|
||||
|
||||
def begin_layer(self, name):
|
||||
self.file.write(f'<g id="{name}" label="{name}" ')
|
||||
self.file.write('inkscape:groupmode="layer">\n')
|
||||
|
||||
def end_layer(self):
|
||||
self.file.write('</g>\n')
|
||||
|
||||
def write(self, raw_code):
|
||||
self.file.write(raw_code)
|
|
@ -8,7 +8,9 @@ from typing import Optional
|
|||
|
||||
|
||||
def parse_options(args):
|
||||
|
||||
"""
|
||||
Parse Röntgen command-line options.
|
||||
"""
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument(
|
||||
|
@ -56,9 +58,9 @@ def parse_options(args):
|
|||
"--show-index", dest="show_index", action="store_true")
|
||||
parser.add_argument(
|
||||
"--no-show-index", dest="show_index", action="store_false")
|
||||
parser.add_argument("--mode", dest="mode", default="normal")
|
||||
parser.add_argument("--seed", dest="seed", default="")
|
||||
parser.add_argument("--level", dest="level", default=None)
|
||||
parser.add_argument("--mode", default="normal")
|
||||
parser.add_argument("--seed", default="")
|
||||
parser.add_argument("--level", default=None)
|
||||
|
||||
arguments = parser.parse_args(args[1:])
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue