Version with save option
This commit is contained in:
parent
1702cce9d1
commit
7d11274be8
13 changed files with 1150 additions and 132 deletions
30
README.md
30
README.md
|
@ -1,3 +1,20 @@
|
||||||
|
server.py
|
||||||
|
: Server for the web interface
|
||||||
|
|
||||||
|
create_shapes.py
|
||||||
|
: Create an svg file per image, with classes on the shapes according to their classes
|
||||||
|
|
||||||
|
zoom_animation.py
|
||||||
|
: Create svg frames for a category. Ordered by the area of the shapes.
|
||||||
|
|
||||||
|
generate_lonely_segments.py
|
||||||
|
: Find and download the images with only one object in them.
|
||||||
|
|
||||||
|
tools.py
|
||||||
|
: Turn a COCO json file (eg `instances_val2017.json`) into a database format (eg `coco_train.db`)
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
Build array of images sorted by size:
|
Build array of images sorted by size:
|
||||||
|
|
||||||
|
@ -20,3 +37,16 @@ cd ../dog_png
|
||||||
ffmpeg -f image2 -pattern_type glob -i '*.png' ../dog.mp4
|
ffmpeg -f image2 -pattern_type glob -i '*.png' ../dog.mp4
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# To run as server:
|
||||||
|
```bash
|
||||||
|
cp plottingdata_coco.service /etc/systemd/system/
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable plottingdata_coco.service
|
||||||
|
systemctl start plottingdata_coco.service
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
rsync . --exclude zoom --exclude venv --exclude archive -av here.rubenvandeven.com:/home/ruben/coco/ --exclude shapes --exclude lonely --exclude .git --exclude __pycache__ --info progress2
|
||||||
|
```
|
||||||
|
|
1
coco.sql
1
coco.sql
|
@ -30,6 +30,7 @@ CREATE TABLE IF NOT EXISTS "annotations" (
|
||||||
"bbox_left" FLOAT,
|
"bbox_left" FLOAT,
|
||||||
"bbox_width" FLOAT,
|
"bbox_width" FLOAT,
|
||||||
"bbox_height" FLOAT,
|
"bbox_height" FLOAT,
|
||||||
|
"zerkine_moments" TEXT DEFAULT NULL,
|
||||||
PRIMARY KEY("id")
|
PRIMARY KEY("id")
|
||||||
) WITHOUT ROWID;
|
) WITHOUT ROWID;
|
||||||
CREATE INDEX IF NOT EXISTS "segments_annotation" ON "segments" (
|
CREATE INDEX IF NOT EXISTS "segments_annotation" ON "segments" (
|
||||||
|
|
9
plottingdata_coco.service
Normal file
9
plottingdata_coco.service
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[Unit]
|
||||||
|
Description=PlottingData COCO interface
|
||||||
|
[Service]
|
||||||
|
ExecStart=/home/ruben/coco/venv/bin/python /home/ruben/coco/server.py --db /home/ruben/coco/coco_train.db --port 8080
|
||||||
|
WorkingDirectory=/home/ruben/coco
|
||||||
|
User=www-data
|
||||||
|
Restart=on-failure
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
5
requirements.txt
Normal file
5
requirements.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
tornado
|
||||||
|
coloredlogs
|
||||||
|
pycocotools
|
||||||
|
numpy
|
||||||
|
mahotas
|
104
server.py
104
server.py
|
@ -7,6 +7,9 @@ import coloredlogs
|
||||||
from coco.storage import COCOStorage
|
from coco.storage import COCOStorage
|
||||||
import json
|
import json
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
import uuid
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
|
||||||
logger = logging.getLogger('coco.server')
|
logger = logging.getLogger('coco.server')
|
||||||
|
|
||||||
|
@ -26,6 +29,9 @@ class RestHandler(tornado.web.RequestHandler):
|
||||||
def get(self, *params):
|
def get(self, *params):
|
||||||
self.write(json.dumps(self.getData(*params), cls=JsonEncoder))
|
self.write(json.dumps(self.getData(*params), cls=JsonEncoder))
|
||||||
|
|
||||||
|
def post(self, *params):
|
||||||
|
self.write(json.dumps(self.getData(*params), cls=JsonEncoder))
|
||||||
|
|
||||||
class CategoryHandler(RestHandler):
|
class CategoryHandler(RestHandler):
|
||||||
def getData(self):
|
def getData(self):
|
||||||
return self.storage.getCategories()
|
return self.storage.getCategories()
|
||||||
|
@ -53,24 +59,89 @@ class AnnotationHandler(RestHandler):
|
||||||
return annotation
|
return annotation
|
||||||
|
|
||||||
|
|
||||||
class WebSocketHandler(tornado.websocket.WebSocketHandler):
|
class SaveHandler(RestHandler):
|
||||||
CORS_ORIGINS = ['localhost', 'coco.local', 'r3.local']
|
def getData(self):
|
||||||
|
"""
|
||||||
|
Save an SVG. Regenerate it on the server to prevent any maliscious input
|
||||||
|
"""
|
||||||
|
req = tornado.escape.json_decode(self.request.body)
|
||||||
|
scene = int(req['scene'])
|
||||||
|
annotations = []
|
||||||
|
|
||||||
def check_origin(self, origin):
|
with open('www/canvas_patterns.json') as fp:
|
||||||
parsed_origin = urlparse(origin)
|
patterns = json.load(fp)
|
||||||
# parsed_origin.netloc.lower() gives localhost:3333
|
svgGs = []
|
||||||
valid = parsed_origin.hostname in self.CORS_ORIGINS
|
|
||||||
return valid
|
|
||||||
|
|
||||||
# the client connected
|
for annotation in req['annotations'][:100]: # max 200 annotations
|
||||||
def open(self, p = None):
|
annId = int(annotation['id'])
|
||||||
WebSocketHandler.connections.add(self)
|
ann = self.storage.getAnnotationById(annId)
|
||||||
logger.info("New client connected")
|
normalisedAnn = ann.getNormalised(100,100)
|
||||||
self.write_message("hello!")
|
x = float(annotation['x'])
|
||||||
|
y = float(annotation['y'])
|
||||||
|
fill = patterns[str(ann.category_id)]
|
||||||
|
segments = []
|
||||||
|
|
||||||
# the client sent the message
|
textX = normalisedAnn.bbox[2]+5
|
||||||
def on_message(self, message):
|
textY = normalisedAnn.bbox[3]
|
||||||
logger.debug(f"recieve: {message}")
|
|
||||||
|
cat = self.storage.getCategory(ann.category_id)
|
||||||
|
image = self.storage.getImage(ann.image_id)
|
||||||
|
|
||||||
|
for segment in normalisedAnn.segments:
|
||||||
|
d = segment.getD()
|
||||||
|
segments.append(f"""<path fill="{fill}" d="{d}"></path>""")
|
||||||
|
svgGs.append(f"""
|
||||||
|
<g data-id="{ann.id}" transform="translate({x},{y})">
|
||||||
|
<image href="{image ['coco_url']}"
|
||||||
|
width="{image['width']*normalisedAnn.scale}"
|
||||||
|
height="{image['height']*normalisedAnn.scale}"
|
||||||
|
x="{ann.bbox[0] * -1 * normalisedAnn.scale}"
|
||||||
|
y="{ann.bbox[1] * -1 * normalisedAnn.scale}"></image>
|
||||||
|
{"".join(segments)}
|
||||||
|
<text fill="white" font-size="40pt" font-family="sans-serif" x="{textX}" y="{textY}">{cat['name']}</text>
|
||||||
|
</g>
|
||||||
|
""")
|
||||||
|
annotations.append({'id': annId, 'x': x, 'y': y})
|
||||||
|
|
||||||
|
source = json.dumps({
|
||||||
|
'scene': scene,
|
||||||
|
'annotations': annotations
|
||||||
|
})
|
||||||
|
|
||||||
|
with open('www/canvas.svg') as fp:
|
||||||
|
svgContent = fp.read()
|
||||||
|
svgContent = svgContent.replace('{source}', json.dumps(source))\
|
||||||
|
.replace('</svg>', "".join(svgGs)+"</svg>")
|
||||||
|
|
||||||
|
|
||||||
|
saveId = uuid.uuid4().hex + '.svg'
|
||||||
|
filename = os.path.join('www/saved', saveId)
|
||||||
|
with open(filename, 'w') as fp:
|
||||||
|
fp.write(svgContent)
|
||||||
|
return {'submission':'/saved/'+saveId}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SavedHandler(tornado.web.RequestHandler):
|
||||||
|
def initialize(self, storage: COCOStorage):
|
||||||
|
self.storage = storage
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
images = []
|
||||||
|
files = glob.glob("www/saved/*.svg")
|
||||||
|
files.sort(key=lambda f: -1 * os.path.getmtime(f))
|
||||||
|
|
||||||
|
for filename in files[:100]:
|
||||||
|
with open(filename, 'r') as fp:
|
||||||
|
# remove first XML line:
|
||||||
|
contents = '\n'.join(fp.read().split('\n')[1:])
|
||||||
|
images.append(contents)
|
||||||
|
|
||||||
|
with open("www/saved.html") as fp:
|
||||||
|
template = fp.read()
|
||||||
|
|
||||||
|
template = template.replace("{images}", ''.join(images))
|
||||||
|
self.write(template)
|
||||||
|
|
||||||
|
|
||||||
class StaticFileWithHeaderHandler(tornado.web.StaticFileHandler):
|
class StaticFileWithHeaderHandler(tornado.web.StaticFileHandler):
|
||||||
|
@ -83,9 +154,10 @@ def make_app(db_filename, debug):
|
||||||
storage = COCOStorage(db_filename)
|
storage = COCOStorage(db_filename)
|
||||||
|
|
||||||
return tornado.web.Application([
|
return tornado.web.Application([
|
||||||
(r"/ws(.*)", WebSocketHandler),
|
|
||||||
(r"/categories.json", CategoryHandler, {'storage': storage}),
|
(r"/categories.json", CategoryHandler, {'storage': storage}),
|
||||||
(r"/annotation.json", AnnotationHandler, {'storage': storage}),
|
(r"/annotation.json", AnnotationHandler, {'storage': storage}),
|
||||||
|
(r"/save", SaveHandler, {'storage': storage}),
|
||||||
|
(r"/saved", SavedHandler, {'storage': storage}),
|
||||||
(r"/(.*)", StaticFileWithHeaderHandler,
|
(r"/(.*)", StaticFileWithHeaderHandler,
|
||||||
{"path": 'www', "default_filename": 'index.html'}),
|
{"path": 'www', "default_filename": 'index.html'}),
|
||||||
], debug=debug)
|
], debug=debug)
|
||||||
|
|
219
tools.py
219
tools.py
|
@ -4,6 +4,16 @@ import logging
|
||||||
import os
|
import os
|
||||||
import pprint
|
import pprint
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
from coco.storage import COCOStorage, Annotation, Segment
|
||||||
|
import cv2
|
||||||
|
import mahotas
|
||||||
|
import subprocess
|
||||||
|
import tqdm
|
||||||
|
import numpy as np
|
||||||
|
import ast
|
||||||
|
import svgwrite
|
||||||
|
from svgwrite.extensions import Inkscape
|
||||||
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
logger = logging.getLogger("coco")
|
logger = logging.getLogger("coco")
|
||||||
|
@ -20,10 +30,34 @@ argParser.add_argument(
|
||||||
help='Show categories'
|
help='Show categories'
|
||||||
)
|
)
|
||||||
argParser.add_argument(
|
argParser.add_argument(
|
||||||
'--propagate',
|
'--db',
|
||||||
type=str,
|
type=str,
|
||||||
metavar='DATABASE',
|
metavar='DATABASE',
|
||||||
help='Store data in sqlite db'
|
help='SQLite db filename, will be created if not existing'
|
||||||
|
)
|
||||||
|
argParser.add_argument(
|
||||||
|
'--propagate',
|
||||||
|
action='store_true',
|
||||||
|
help='Store annotation data in sqlite db'
|
||||||
|
)
|
||||||
|
argParser.add_argument(
|
||||||
|
'--zerkine',
|
||||||
|
action='store_true',
|
||||||
|
help='Find and store annotation Zerkine moments for those that do not have it yet'
|
||||||
|
)
|
||||||
|
argParser.add_argument(
|
||||||
|
'--similar',
|
||||||
|
type=int,
|
||||||
|
metavar="ANNOTATION_ID",
|
||||||
|
help='Find similar shapes for annotation'
|
||||||
|
)
|
||||||
|
argParser.add_argument(
|
||||||
|
'--stickers',
|
||||||
|
type=str,
|
||||||
|
metavar="SVG_FILENAME",
|
||||||
|
help="""
|
||||||
|
Create an SVG with sticker pages (afterwards convert to EPS: \"for f in *; do echo $f; inkscape -f $f --export-eps $f.eps; done\")
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
args = argParser.parse_args()
|
args = argParser.parse_args()
|
||||||
|
|
||||||
|
@ -41,15 +75,12 @@ if args.categories:
|
||||||
# pp = pprint.PrettyPrinter(indent=4)
|
# pp = pprint.PrettyPrinter(indent=4)
|
||||||
pprint.pprint(cats, sort_dicts=False)
|
pprint.pprint(cats, sort_dicts=False)
|
||||||
|
|
||||||
if args.propagate:
|
storage = None
|
||||||
if not os.path.exists(args.propagate):
|
if args.db:
|
||||||
con = sqlite3.connect(args.propagate)
|
storage = COCOStorage(args.db)
|
||||||
cur = con.cursor()
|
con = storage.con
|
||||||
with open('coco.sql', 'r') as fp:
|
|
||||||
cur.executescript(fp.read())
|
|
||||||
con.close()
|
|
||||||
|
|
||||||
con = sqlite3.connect(args.propagate)
|
if args.propagate:
|
||||||
logger.info("Create categories")
|
logger.info("Create categories")
|
||||||
cur = con.cursor()
|
cur = con.cursor()
|
||||||
cur.executemany('INSERT OR IGNORE INTO categories(id, supercategory, name) VALUES (:id, :supercategory, :name)', coco.cats.values())
|
cur.executemany('INSERT OR IGNORE INTO categories(id, supercategory, name) VALUES (:id, :supercategory, :name)', coco.cats.values())
|
||||||
|
@ -101,6 +132,168 @@ if args.propagate:
|
||||||
|
|
||||||
logger.info("Done...")
|
logger.info("Done...")
|
||||||
|
|
||||||
# for id, cat in coco.cats.items():
|
if args.zerkine:
|
||||||
# cur = con.cursor()
|
nr = storage.countAnnotationsWithoutZerkine()
|
||||||
# cur.execute
|
for i in tqdm.tqdm(range(nr)):
|
||||||
|
annotation = storage.getAnnotationWithoutZerkine()
|
||||||
|
normAnn = annotation.getNormalised(100, 100)
|
||||||
|
filenameRoot = '/tmp/tmp_ann_to_convert'
|
||||||
|
dwg = normAnn.asSvg(filenameRoot + '.svg', square=True, bg='black')
|
||||||
|
dwg.save()
|
||||||
|
# convert to rasterised
|
||||||
|
subprocess.call([
|
||||||
|
'inkscape',
|
||||||
|
'-f', filenameRoot + '.svg',
|
||||||
|
'-e', filenameRoot + '.png',
|
||||||
|
],
|
||||||
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||||
|
)
|
||||||
|
# read with opencv
|
||||||
|
image = cv2.imread(filenameRoot + '.png', cv2.IMREAD_GRAYSCALE)
|
||||||
|
moments = mahotas.features.zernike_moments(image, 21)
|
||||||
|
storage.storeZerkineForAnnotation(annotation, moments, delayCommit = True)
|
||||||
|
if not i % 100:
|
||||||
|
storage.con.commit()
|
||||||
|
storage.con.commit()
|
||||||
|
|
||||||
|
if args.similar:
|
||||||
|
# todo find similar
|
||||||
|
annotation = storage.getAnnotationById(args.similar, withZerkine=True)
|
||||||
|
dwg = annotation.asSvg(f'tmp/source.svg', square=True)
|
||||||
|
dwg.save()
|
||||||
|
|
||||||
|
shapeA = np.array(annotation.segments[0].points)
|
||||||
|
|
||||||
|
annMoments = np.array(annotation.zerkine_moment)
|
||||||
|
distances = []
|
||||||
|
print(annotation)
|
||||||
|
# Stupid, this seems to have been superfluous
|
||||||
|
# zerkines = storage.getZerkines()
|
||||||
|
# for zerkine in tqdm.tqdm(zerkines):
|
||||||
|
# if annotation.id == zerkine['id']:
|
||||||
|
# continue
|
||||||
|
#
|
||||||
|
# diff = annMoments - np.array(Annotation.parseZerkineFromDB(zerkine['zerkine_moment']))
|
||||||
|
# distance = np.linalg.norm(diff)
|
||||||
|
# distances.append((zerkine['id'], distance))
|
||||||
|
anns = storage.getAllAnnotationPoints()
|
||||||
|
for ann in tqdm.tqdm(anns):
|
||||||
|
try:
|
||||||
|
shapeB = np.array(Segment.asCoordinates(ast.literal_eval('['+ann['points']+']')))
|
||||||
|
# fourth param is require, but according to docs does nothing
|
||||||
|
distance= cv2.matchShapes(shapeA, shapeB, cv2.CONTOURS_MATCH_I2, 1)
|
||||||
|
distances.append((ann['id'], distance))
|
||||||
|
except Exception as e:
|
||||||
|
logger.critical(f"Exception comparing {annotation.id} to {ann['id']}, points: {ann['points']}")
|
||||||
|
logger.exception(e)
|
||||||
|
|
||||||
|
distances = sorted(distances, key=lambda d: d[1])
|
||||||
|
|
||||||
|
for i in range(10):
|
||||||
|
similarAnnotation = storage.getAnnotationById(distances[i][0])
|
||||||
|
print(coco.cats[similarAnnotation.category_id])
|
||||||
|
dwg = similarAnnotation.asSvg(f'tmp/result_{i}.svg', square=True)
|
||||||
|
dwg.save()
|
||||||
|
|
||||||
|
if args.stickers:
|
||||||
|
grid = (3, 4) # items in the grid x,y
|
||||||
|
size = (105, 148) # in mm
|
||||||
|
sizeFactor = 5 # influences the size of the patterns
|
||||||
|
viewBoxSize = (size[0] * sizeFactor, size[1] * sizeFactor)
|
||||||
|
margin = 5
|
||||||
|
gridSize = (
|
||||||
|
int((viewBoxSize[0]-((grid[0]+1)*margin))/grid[0]),
|
||||||
|
int((viewBoxSize[1]-((grid[1]+1)*margin))/grid[1])
|
||||||
|
)
|
||||||
|
|
||||||
|
# see also textures.xml
|
||||||
|
textureIds = ["#siqwx","#wjnbs","#pnfez","#ejtxy","#obabs","#hehoj","#mrwjs","#ryjbw","#rkkau","#vbjcl","#zzehx","#mumke","#brhhk","#gujvh","#hfgqa","#lrbsh","#bndby","#bfnxk","#ydler","#pnxdr","#htqlj","#nunnt","#tidaw","#tcdum","#kwwja","#hgdkl","#nvkwz","#uzdqb","#fgshk","#vknil","#yeenr","#mslkw","#eibaw","#meama","#akuvz","#khkpp","#ibnow","#wivvx","#svksy","#xhmew","#jmiqu","#gfcer","#iueil","#iufvt","#ugkud","#dchzd","#nejks","#dqseb","#yhrwm","#bmiet","#qovkk","#hxoiq","#jfguh","#kbpkl","#ikarj","#nucap","#qfsqn","#bboqt","#pxkjn","#lbnx","#nxkmp","#snojb","#oioil","#hvldz","#qpscp","#oborh","#crobu","#ydhwn","#geanf","#sdfeo","#cgtma","#rjfrc","#uhcys","#lrgem","#osiho","#etssd","#esxcs","#hczhr","#nnhxw","#wrlbu"]
|
||||||
|
|
||||||
|
nr = 0
|
||||||
|
total_nr = len(coco.cats)
|
||||||
|
for category_id, cat in coco.cats.items():
|
||||||
|
nr+=1
|
||||||
|
filename = os.path.join(
|
||||||
|
args.stickers,
|
||||||
|
f"{category_id}_{cat['supercategory']}_{cat['name']}.svg")
|
||||||
|
dwg = svgwrite.Drawing(
|
||||||
|
filename,
|
||||||
|
size=(f'{size[0]}mm', f'{size[1]}mm'),
|
||||||
|
viewBox=f"0 0 {viewBoxSize[0]} {viewBoxSize[1]}"
|
||||||
|
)
|
||||||
|
|
||||||
|
annotations = storage.getRandomAnnotations(
|
||||||
|
limit = grid[0]*grid[1],
|
||||||
|
category_id = category_id
|
||||||
|
)
|
||||||
|
|
||||||
|
inkscape = Inkscape(dwg)
|
||||||
|
contourG = inkscape.layer(label='Snijlijnen')
|
||||||
|
drawingG = inkscape.layer(label='Shapes')
|
||||||
|
|
||||||
|
# dwg.add(svgwrite.container.Defs())
|
||||||
|
dwg.add(drawingG)
|
||||||
|
dwg.add(contourG)
|
||||||
|
|
||||||
|
|
||||||
|
font_size = 10
|
||||||
|
text = dwg.text(
|
||||||
|
f"{nr:02d}/{total_nr}",
|
||||||
|
insert=(margin, margin+font_size), font_size=font_size, fill='black'
|
||||||
|
)
|
||||||
|
drawingG.add(text)
|
||||||
|
|
||||||
|
text = dwg.text(
|
||||||
|
f"{category_id}. {cat['supercategory']} - {cat['name']}",
|
||||||
|
insert=(viewBoxSize[0]-margin, margin+font_size), font_size=font_size, fill='black',
|
||||||
|
style='text-anchor:end;')
|
||||||
|
drawingG.add(text)
|
||||||
|
|
||||||
|
text = dwg.text(
|
||||||
|
f"Common Objects In Context",
|
||||||
|
insert=(margin, viewBoxSize[1]-margin), font_size=font_size, fill='black',
|
||||||
|
)
|
||||||
|
drawingG.add(text)
|
||||||
|
|
||||||
|
text = dwg.text(
|
||||||
|
f"Plotting Data",
|
||||||
|
insert=(viewBoxSize[0]-margin, viewBoxSize[1]-margin), font_size=font_size, fill='black',
|
||||||
|
style='text-anchor:end;')
|
||||||
|
drawingG.add(text)
|
||||||
|
|
||||||
|
for i, annotation in enumerate(annotations):
|
||||||
|
normAnn = annotation.getNormalised(gridSize[0], gridSize[1])
|
||||||
|
translation = normAnn.getTranslationToCenter()
|
||||||
|
# print(translation)
|
||||||
|
|
||||||
|
pX = i%grid[0]
|
||||||
|
pY = int(i/grid[0])
|
||||||
|
posX = pX*gridSize[0] + (pX+1)*margin - translation[0]
|
||||||
|
posY = pY*gridSize[1] + (pY+1)*margin - translation[1]
|
||||||
|
# print(i, posX, posY, gridSize)
|
||||||
|
|
||||||
|
|
||||||
|
positionG = svgwrite.container.Group(transform=f'translate({posX}, {posY})')
|
||||||
|
normAnn.writeToDrawing(positionG, stroke='#2FEE2F', stroke_width='1pt', fill_opacity="0")
|
||||||
|
contourG.add(positionG)
|
||||||
|
|
||||||
|
position2G = svgwrite.container.Group(transform=f'translate({posX}, {posY})')
|
||||||
|
pattern_id = textureIds[category_id % len(textureIds)]
|
||||||
|
normAnn.writeToDrawing(position2G, fill=f'url({pattern_id})', stroke='blue', stroke_width='0')
|
||||||
|
drawingG.add(position2G)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
xml = dwg.get_xml()
|
||||||
|
with open('textures.xml', 'r') as fp:
|
||||||
|
textureTree = ElementTree.fromstring(fp.read())
|
||||||
|
defsTree = xml.find('defs')
|
||||||
|
for pattern in textureTree:
|
||||||
|
defsTree.append(pattern)
|
||||||
|
xmlString = ElementTree.tostring(xml)
|
||||||
|
with open(filename, 'wb') as fp:
|
||||||
|
# print(xmlString)
|
||||||
|
fp.write(xmlString)
|
||||||
|
logger.info(f"Wrote to {filename}")
|
||||||
|
|
||||||
|
|
309
www/canvas.svg
Normal file
309
www/canvas.svg
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" id="svgCanvas" viewBox="0 0 1000 1000">
|
||||||
|
<metadata><rdf:RDF><dc:source>{source}</dc:source></rdf:RDF></metadata>
|
||||||
|
<defs>
|
||||||
|
<pattern id="tprke" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#FA0800" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="none" stroke="#FA0800" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="5" r="2" fill="none" stroke="#FA0800" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="0" r="2" fill="none" stroke="#FA0800" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="5" r="2" fill="none" stroke="#FA0800" stroke-width="1" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="szylj" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#80FF00" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="ooumd" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||||
|
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#80FF00" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="fheyp" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||||
|
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#80FF00" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="ssscs" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#80FF00" stroke-linecap="square" />
|
||||||
|
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#80FF00" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="htafw" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||||
|
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#80FF00" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="mwmpe" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<circle cx="2" cy="2" r="2" fill="#80FF00" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="ipzlm" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<circle cx="5" cy="5" r="2" fill="#80FF00" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="#80FF00" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="10" r="2" fill="#80FF00" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="0" r="2" fill="#80FF00" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="10" r="2" fill="#80FF00" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="ryfgx" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#80FF00" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="gktcz" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||||
|
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#00FFFF" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="nkuai" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#00FFFF" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="tnder" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#00FFFF" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="hfgks" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||||
|
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#00FFFF" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="mrlhp" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||||
|
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#00FFFF" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="cmsda" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#FF00FF" stroke-linecap="square" />
|
||||||
|
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#FF00FF" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="wsewh" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||||
|
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#FF00FF" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="htoli" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<circle cx="2" cy="2" r="2" fill="#FF00FF" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="aoggk" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<circle cx="5" cy="5" r="2" fill="#FF00FF" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="#FF00FF" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="10" r="2" fill="#FF00FF" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="0" r="2" fill="#FF00FF" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="10" r="2" fill="#FF00FF" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="mfcrf" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#FF00FF" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="igtrq" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||||
|
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#FF00FF" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="wgyom" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#FF00FF" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="qxsnm" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#FF00FF" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="none" stroke="#FF00FF" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="5" r="2" fill="none" stroke="#FF00FF" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="0" r="2" fill="none" stroke="#FF00FF" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="5" r="2" fill="none" stroke="#FF00FF" stroke-width="1" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="gmgmx" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#FF00FF" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="qubbv" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||||
|
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#FF00FF" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="ttene" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#FFFF00" stroke-linecap="square" />
|
||||||
|
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#FFFF00" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="fazqm" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||||
|
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#FFFF00" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="nfcwz" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#FFFF00" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="rwmct" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||||
|
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#FFFF00" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="iiitd" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#FFFF00" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="eajyr" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#0080FF" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="none" stroke="#0080FF" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="5" r="2" fill="none" stroke="#0080FF" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="0" r="2" fill="none" stroke="#0080FF" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="5" r="2" fill="none" stroke="#0080FF" stroke-width="1" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="aknuz" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#0080FF" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="vhjmw" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||||
|
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#0080FF" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="mrist" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||||
|
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#0080FF" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="mbbhg" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#0080FF" stroke-linecap="square" />
|
||||||
|
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#0080FF" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="hfuqj" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||||
|
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#0080FF" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="jrwrh" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<circle cx="2" cy="2" r="2" fill="#0080FF" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="bncvb" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<circle cx="5" cy="5" r="2" fill="#0080FF" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="#0080FF" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="10" r="2" fill="#0080FF" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="0" r="2" fill="#0080FF" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="10" r="2" fill="#0080FF" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="upgob" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#0080FF" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="krnlo" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||||
|
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#0080FF" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="sgkgd" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#FA8500" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="mfhbm" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#FA8500" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="skgis" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||||
|
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#FA8500" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="oswbs" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||||
|
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#FA8500" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="lsouj" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#FA8500" stroke-linecap="square" />
|
||||||
|
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#FA8500" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="bvkgt" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||||
|
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#FA8500" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="fgivr" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<circle cx="2" cy="2" r="2" fill="#FA8500" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="yfcyp" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<circle cx="5" cy="5" r="2" fill="#00FA85" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="#00FA85" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="10" r="2" fill="#00FA85" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="0" r="2" fill="#00FA85" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="10" r="2" fill="#00FA85" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="gmbhz" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#00FA85" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="yfydn" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||||
|
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#00FA85" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="gxqls" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#00FA85" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="cfbwj" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#00FA85" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="none" stroke="#00FA85" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="5" r="2" fill="none" stroke="#00FA85" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="0" r="2" fill="none" stroke="#00FA85" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="5" r="2" fill="none" stroke="#00FA85" stroke-width="1" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="fsgnv" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#00FA85" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="vakgp" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||||
|
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#00FA85" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="oaprx" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||||
|
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#00FA85" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="fxicb" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#00FA85" stroke-linecap="square" />
|
||||||
|
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#00FA85" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="hjala" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||||
|
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#00FA85" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="xjxrr" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<circle cx="2" cy="2" r="2" fill="#BB00FA" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="yzmaq" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<circle cx="5" cy="5" r="2" fill="#BB00FA" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="#BB00FA" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="10" r="2" fill="#BB00FA" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="0" r="2" fill="#BB00FA" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="10" r="2" fill="#BB00FA" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="dguls" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#BB00FA" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="hltax" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||||
|
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#BB00FA" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="czaje" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#BB00FA" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="none" stroke="#BB00FA" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="5" r="2" fill="none" stroke="#BB00FA" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="0" r="2" fill="none" stroke="#BB00FA" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="5" r="2" fill="none" stroke="#BB00FA" stroke-width="1" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="julqj" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||||
|
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#BB00FA" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="gic" patternUnits="userSpaceOnUse" width="15" height="8.660254037844386">
|
||||||
|
<path d="M 5,0 l 5,0 l 2.5,4.330127018922193 l -2.5,4.330127018922193 l -5,0 l -2.5,-4.330127018922193 Z M 0,4.330127018922193 l 2.5,0 M 15,4.330127018922193 l -2.5,0" fill="#FAD900" stroke="rgba(0,0,0,0)" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="ohqxb" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<circle cx="2" cy="2" r="2" fill="#FAD900" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="bvwec" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<circle cx="5" cy="5" r="2" fill="#FAD900" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="#FAD900" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="10" r="2" fill="#FAD900" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="0" r="2" fill="#FAD900" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="10" r="2" fill="#FAD900" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="jdlwh" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#FAD900" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="wwcdm" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||||
|
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#FAD900" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="wcyvp" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#FAD900" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="ljnqw" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#FF0080" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="none" stroke="#FF0080" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="5" r="2" fill="none" stroke="#FF0080" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="0" r="2" fill="none" stroke="#FF0080" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="5" r="2" fill="none" stroke="#FF0080" stroke-width="1" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="xcdur" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#FF0080" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="nxkhz" patternUnits="userSpaceOnUse" width="32" height="32">
|
||||||
|
<path d="M 0,32 l 32,-32 M -8,8 l 16,-16 M 24,40 l 16,-16" stroke-width="16" shape-rendering="auto" stroke="#FF0080" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="cpnap" patternUnits="userSpaceOnUse" width="9" height="5.196152422706632">
|
||||||
|
<path d="M 3,0 l 3,0 l 1.5,2.598076211353316 l -1.5,2.598076211353316 l -3,0 l -1.5,-2.598076211353316 Z M 0,2.598076211353316 l 1.5,0 M 9,2.598076211353316 l -1.5,0" fill="transparent" stroke="#FF0080" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="lhupq" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 2, 0 l 0, 4" stroke-width="1" shape-rendering="crispEdges" stroke="#FF0080" stroke-linecap="square" />
|
||||||
|
<path d="M 0,2 l 4,0" stroke-width="1" shape-rendering="crispEdges" stroke="#FF0080" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="aaqtb" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<circle cx="2" cy="2" r="2" fill="#8500FA" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="lufrm" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<circle cx="5" cy="5" r="2" fill="#8500FA" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="#8500FA" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="0" cy="10" r="2" fill="#8500FA" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="0" r="2" fill="#8500FA" stroke="#343434" stroke-width="0" />
|
||||||
|
<circle cx="10" cy="10" r="2" fill="#8500FA" stroke="#343434" stroke-width="0" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="azwdr" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<path d="M 1.25,3.75l1.25,-2.5l1.25,2.5" fill="transparent" stroke="#8500FA" stroke-width="1" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="yffat" patternUnits="userSpaceOnUse" width="12" height="6.928203230275509">
|
||||||
|
<path d="M 4,0 l 4,0 l 2,3.4641016151377544 l -2,3.4641016151377544 l -4,0 l -2,-3.4641016151377544 Z M 0,3.4641016151377544 l 2,0 M 12,3.4641016151377544 l -2,0" fill="transparent" stroke="#8500FA" stroke-width="2" stroke-linecap="square" shape-rendering="auto" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="hslpy" patternUnits="userSpaceOnUse" width="4" height="4">
|
||||||
|
<path d="M 0,4 l 4,-4 M -1,1 l 2,-2 M 3,5 l 2,-2" stroke-width="1" shape-rendering="auto" stroke="#8500FA" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="zsvlo" patternUnits="userSpaceOnUse" width="5" height="5">
|
||||||
|
<circle cx="2.5" cy="2.5" r="2" fill="none" stroke="#8500FA" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="0" r="2" fill="none" stroke="#8500FA" stroke-width="1" />
|
||||||
|
<circle cx="0" cy="5" r="2" fill="none" stroke="#8500FA" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="0" r="2" fill="none" stroke="#8500FA" stroke-width="1" />
|
||||||
|
<circle cx="5" cy="5" r="2" fill="none" stroke="#8500FA" stroke-width="1" />
|
||||||
|
</pattern>
|
||||||
|
<pattern id="muhub" patternUnits="userSpaceOnUse" width="10" height="10">
|
||||||
|
<path d="M 0,7.5 l 10,-5 M 0,2.5 l 10,-5 M 0,12.5 l 10,-5" stroke-width="2" shape-rendering="auto" stroke="#8500FA" stroke-linecap="square" />
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 27 KiB |
82
www/canvas_patterns.json
Normal file
82
www/canvas_patterns.json
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
{
|
||||||
|
"1": "url(#tprke)",
|
||||||
|
"2": "url(#szylj)",
|
||||||
|
"3": "url(#ooumd)",
|
||||||
|
"4": "url(#fheyp)",
|
||||||
|
"5": "url(#ssscs)",
|
||||||
|
"6": "url(#htafw)",
|
||||||
|
"7": "url(#mwmpe)",
|
||||||
|
"8": "url(#ipzlm)",
|
||||||
|
"9": "url(#ryfgx)",
|
||||||
|
"10": "url(#gktcz)",
|
||||||
|
"11": "url(#nkuai)",
|
||||||
|
"13": "url(#tnder)",
|
||||||
|
"14": "url(#hfgks)",
|
||||||
|
"15": "url(#mrlhp)",
|
||||||
|
"16": "url(#cmsda)",
|
||||||
|
"17": "url(#wsewh)",
|
||||||
|
"18": "url(#htoli)",
|
||||||
|
"19": "url(#aoggk)",
|
||||||
|
"20": "url(#mfcrf)",
|
||||||
|
"21": "url(#igtrq)",
|
||||||
|
"22": "url(#wgyom)",
|
||||||
|
"23": "url(#qxsnm)",
|
||||||
|
"24": "url(#gmgmx)",
|
||||||
|
"25": "url(#qubbv)",
|
||||||
|
"27": "url(#ttene)",
|
||||||
|
"28": "url(#fazqm)",
|
||||||
|
"31": "url(#nfcwz)",
|
||||||
|
"32": "url(#rwmct)",
|
||||||
|
"33": "url(#iiitd)",
|
||||||
|
"34": "url(#eajyr)",
|
||||||
|
"35": "url(#aknuz)",
|
||||||
|
"36": "url(#vhjmw)",
|
||||||
|
"37": "url(#mrist)",
|
||||||
|
"38": "url(#mbbhg)",
|
||||||
|
"39": "url(#hfuqj)",
|
||||||
|
"40": "url(#jrwrh)",
|
||||||
|
"41": "url(#bncvb)",
|
||||||
|
"42": "url(#upgob)",
|
||||||
|
"43": "url(#krnlo)",
|
||||||
|
"44": "url(#sgkgd)",
|
||||||
|
"46": "url(#mfhbm)",
|
||||||
|
"47": "url(#skgis)",
|
||||||
|
"48": "url(#oswbs)",
|
||||||
|
"49": "url(#lsouj)",
|
||||||
|
"50": "url(#bvkgt)",
|
||||||
|
"51": "url(#fgivr)",
|
||||||
|
"52": "url(#yfcyp)",
|
||||||
|
"53": "url(#gmbhz)",
|
||||||
|
"54": "url(#yfydn)",
|
||||||
|
"55": "url(#gxqls)",
|
||||||
|
"56": "url(#cfbwj)",
|
||||||
|
"57": "url(#fsgnv)",
|
||||||
|
"58": "url(#vakgp)",
|
||||||
|
"59": "url(#oaprx)",
|
||||||
|
"60": "url(#fxicb)",
|
||||||
|
"61": "url(#hjala)",
|
||||||
|
"62": "url(#xjxrr)",
|
||||||
|
"63": "url(#yzmaq)",
|
||||||
|
"64": "url(#dguls)",
|
||||||
|
"65": "url(#hltax)",
|
||||||
|
"67": "url(#czaje)",
|
||||||
|
"70": "url(#julqj)",
|
||||||
|
"72": "url(#gic)",
|
||||||
|
"73": "url(#ohqxb)",
|
||||||
|
"74": "url(#bvwec)",
|
||||||
|
"75": "url(#jdlwh)",
|
||||||
|
"76": "url(#wwcdm)",
|
||||||
|
"77": "url(#wcyvp)",
|
||||||
|
"78": "url(#ljnqw)",
|
||||||
|
"79": "url(#xcdur)",
|
||||||
|
"80": "url(#nxkhz)",
|
||||||
|
"81": "url(#cpnap)",
|
||||||
|
"82": "url(#lhupq)",
|
||||||
|
"84": "url(#aaqtb)",
|
||||||
|
"85": "url(#lufrm)",
|
||||||
|
"86": "url(#azwdr)",
|
||||||
|
"87": "url(#yffat)",
|
||||||
|
"88": "url(#hslpy)",
|
||||||
|
"89": "url(#zsvlo)",
|
||||||
|
"90": "url(#muhub)"
|
||||||
|
}
|
155
www/coco.css
Normal file
155
www/coco.css
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
body{
|
||||||
|
font-family:sans-serif;
|
||||||
|
background: darkblue;
|
||||||
|
margin:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#svgCanvas{
|
||||||
|
width:100vw;
|
||||||
|
height:100vh;
|
||||||
|
}
|
||||||
|
.catNav{
|
||||||
|
position:absolute;
|
||||||
|
top:0;
|
||||||
|
color:white;
|
||||||
|
bottom:0;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
#catNav {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
#catNav2 {
|
||||||
|
right:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.catNav ul{
|
||||||
|
list-style:none;
|
||||||
|
padding:0px;
|
||||||
|
margin: 30px 0px;
|
||||||
|
border-width: 3px;
|
||||||
|
}
|
||||||
|
#catNav ul {
|
||||||
|
border-left-style: solid;
|
||||||
|
}
|
||||||
|
#catNav2 ul {
|
||||||
|
border-right-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.catNav li{
|
||||||
|
padding:2px;
|
||||||
|
}
|
||||||
|
.catNav li:hover{
|
||||||
|
cursor:pointer;
|
||||||
|
text-decoration:underline;
|
||||||
|
}
|
||||||
|
#catNav li{
|
||||||
|
transform: rotate(-30deg);
|
||||||
|
transform-origin: top left;
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
#catNav2 li{
|
||||||
|
transform: rotate(30deg);
|
||||||
|
text-align:right;
|
||||||
|
transform-origin: top right;
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
g{
|
||||||
|
cursor:grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
g:active{
|
||||||
|
cursor:grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg text{
|
||||||
|
fill:white;
|
||||||
|
font-size: 40pt;
|
||||||
|
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-khtml-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons{
|
||||||
|
position:absolute;
|
||||||
|
top: 10px;
|
||||||
|
text-align:center;
|
||||||
|
width: 300px;
|
||||||
|
left: calc(50% - 150px);
|
||||||
|
}
|
||||||
|
.buttons.buttons-inline{
|
||||||
|
position:static;
|
||||||
|
margin: 10px;
|
||||||
|
width:100%;
|
||||||
|
}
|
||||||
|
.buttons span, .buttons label{
|
||||||
|
color:white;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
.buttons span:hover{
|
||||||
|
text-decoration:underline;
|
||||||
|
}
|
||||||
|
.buttons #sceneLabel{
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.hideImages svg image{
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
body.hideLabels svg text{
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.supercategory-person{
|
||||||
|
border-color:rgb(250,8,0);
|
||||||
|
}
|
||||||
|
.supercategory-vehicle{
|
||||||
|
border-color:rgb(128,255,0);
|
||||||
|
}
|
||||||
|
.supercategory-outdoor{
|
||||||
|
border-color:rgb(0,255,255);
|
||||||
|
}
|
||||||
|
.supercategory-animal{
|
||||||
|
border-color:rgb(255,0,255);
|
||||||
|
}
|
||||||
|
.supercategory-accessory{
|
||||||
|
border-color:rgb(255,255,0);
|
||||||
|
}
|
||||||
|
.supercategory-sports{
|
||||||
|
border-color:rgb(0,128,255);
|
||||||
|
}
|
||||||
|
.supercategory-kitchen{
|
||||||
|
border-color:rgb(250,133,0);
|
||||||
|
}
|
||||||
|
.supercategory-furniture{
|
||||||
|
border-color:rgb(187,0,250);
|
||||||
|
}
|
||||||
|
.supercategory-food{
|
||||||
|
border-color:rgb(0,250,133);
|
||||||
|
}
|
||||||
|
.supercategory-electronic{
|
||||||
|
border-color:rgb(250,217,0);
|
||||||
|
}
|
||||||
|
.supercategory-appliance{
|
||||||
|
border-color:rgb(255,0,128);
|
||||||
|
}
|
||||||
|
.supercategory-indoor{
|
||||||
|
border-color:rgb(133,0,250);
|
||||||
|
}
|
||||||
|
|
||||||
|
#save{margin: 5px;}
|
||||||
|
|
||||||
|
#saved{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#saved svg{
|
||||||
|
width: 30vw;
|
||||||
|
height: 20vw;
|
||||||
|
margin: 10px;
|
||||||
|
border:solid 1px white;
|
||||||
|
}
|
202
www/coco.js
202
www/coco.js
|
@ -58,9 +58,9 @@ function getColoredTexture(i, color) {
|
||||||
case 6:
|
case 6:
|
||||||
return textures.paths().d("hexagons").size(5).strokeWidth(2).stroke("rgba(0,0,0,0)").fill(color);
|
return textures.paths().d("hexagons").size(5).strokeWidth(2).stroke("rgba(0,0,0,0)").fill(color);
|
||||||
case 7:
|
case 7:
|
||||||
return textures.circles().size(4).stroke(color);
|
return textures.circles().size(4).fill(color);
|
||||||
case 8:
|
case 8:
|
||||||
return textures.circles().thicker().complement().stroke(color);
|
return textures.circles().thicker().complement().fill(color);
|
||||||
case 9:
|
case 9:
|
||||||
return textures.paths().d("caps").lighter().thicker().size(5).stroke(color);
|
return textures.paths().d("caps").lighter().thicker().size(5).stroke(color);
|
||||||
case 10:
|
case 10:
|
||||||
|
@ -70,17 +70,18 @@ function getColoredTexture(i, color) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const categoryColors = {
|
const categoryColors = {
|
||||||
"person": "#f00",
|
"person": "#FA0800",
|
||||||
"vehicle": "#0f0",
|
"vehicle": "#80FF00",
|
||||||
"outdoor": "#006",
|
"outdoor": "#00FFFF",
|
||||||
"animal": "#ff0",
|
"animal": "#FF00FF",
|
||||||
"food": "#0ff",
|
"accessory": "#FFFF00",
|
||||||
"furniture": "#f0f",
|
"food": "#00FA85",
|
||||||
"indoor": "#fff",
|
"furniture": "#BB00FA",
|
||||||
"electronic": "#390",
|
"indoor": "#8500FA",
|
||||||
"kitchen": "#930",
|
"electronic": "#FAD900",
|
||||||
"accessory": "#f90",
|
"kitchen": "#FA8500",
|
||||||
"sports": "#f09",
|
"sports": "#0080FF",
|
||||||
|
"appliance": "#FF0080",
|
||||||
}
|
}
|
||||||
|
|
||||||
function getColorForSuperCategory(name) {
|
function getColorForSuperCategory(name) {
|
||||||
|
@ -103,8 +104,21 @@ function getTextureForCategory(id) {
|
||||||
class CocoCanvas {
|
class CocoCanvas {
|
||||||
start(){
|
start(){
|
||||||
this.catNavEl = document.getElementById('catNav');
|
this.catNavEl = document.getElementById('catNav');
|
||||||
|
this.catNav2El = document.getElementById('catNav2');
|
||||||
this.canvas = document.getElementById('svgCanvas');
|
this.canvas = document.getElementById('svgCanvas');
|
||||||
|
this.loadImagesBtnEl = document.getElementById("loadImages");
|
||||||
|
this.loadLabelsBtnEl = document.getElementById("loadLabels");
|
||||||
|
this.sceneSelectEl = document.getElementById('scene');
|
||||||
|
this.savedBtnEl = document.getElementById('save');
|
||||||
this.loadNav()
|
this.loadNav()
|
||||||
|
this.annotations = [];
|
||||||
|
|
||||||
|
this.loadImagesBtnEl.addEventListener('change', (e) => this.toggleImages(e));
|
||||||
|
this.loadLabelsBtnEl.addEventListener('change', (e) => this.toggleLabels(e));
|
||||||
|
this.savedBtnEl.addEventListener('click', function(e){
|
||||||
|
if(this.savedBtnEl.disabled || this.annotations.length < 1) return;
|
||||||
|
this.save();
|
||||||
|
}.bind(this));
|
||||||
}
|
}
|
||||||
loadNav() {
|
loadNav() {
|
||||||
let r = new Request('/categories.json');
|
let r = new Request('/categories.json');
|
||||||
|
@ -121,40 +135,63 @@ class CocoCanvas {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildNav(categories) {
|
buildNav(categories) {
|
||||||
let ulEl = crel('ul');
|
let lastSuperCat = null;
|
||||||
|
let supercategories = []
|
||||||
|
let supercategoriesEls = {}
|
||||||
|
// create menu's per supercategory, divide these over left & right
|
||||||
for(let cat of categories) {
|
for(let cat of categories) {
|
||||||
ulEl.appendChild(
|
if(supercategories.indexOf(cat['supercategory']) < 0) {
|
||||||
crel('li', {
|
supercategories.push(cat['supercategory']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(let supercat of supercategories) {
|
||||||
|
let ulEl = crel('ul', {'data-name': supercat, 'class':'supercategory-'+supercat});
|
||||||
|
supercategoriesEls[supercat] = ulEl;
|
||||||
|
// first half left, other half right side of the page
|
||||||
|
if(supercategories.indexOf(supercat)/supercategories.length < .5) {
|
||||||
|
this.catNavEl.appendChild(ulEl);
|
||||||
|
} else {
|
||||||
|
this.catNav2El.appendChild(ulEl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// entries for the menus
|
||||||
|
for(let cat of categories) {
|
||||||
|
// let firstOfType = lastSuperCat != cat['supercategory'] ? ' first-of-super' : '';
|
||||||
|
// lastSuperCat = cat['supercategory'];
|
||||||
|
supercategoriesEls[cat['supercategory']].appendChild(crel('li', {
|
||||||
'id': 'category-' + cat['id'],
|
'id': 'category-' + cat['id'],
|
||||||
|
// 'class': 'supercategory-' + cat['supercategory'],
|
||||||
'on': {
|
'on': {
|
||||||
'click': (e) => {
|
'click': (e) => {
|
||||||
this.requestAnnotation(cat['id']);
|
this.requestAnnotation(cat['id']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, cat['name'])
|
}, cat['name']));
|
||||||
);
|
|
||||||
}
|
|
||||||
this.catNavEl.appendChild(ulEl);
|
|
||||||
|
|
||||||
let defsEl = document.createElementNS("http://www.w3.org/2000/svg", 'defs');
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.defsEl = document.createElementNS("http://www.w3.org/2000/svg", 'defs');
|
||||||
for(let cat of categories) {
|
for(let cat of categories) {
|
||||||
let texture = getTextureForCategory(cat['id']);
|
let texture = getTextureForCategory(cat['id']);
|
||||||
let sel = new dom();
|
let sel = new dom();
|
||||||
texture(sel);
|
texture(sel);
|
||||||
defsEl.innerHTML += sel.toString();
|
this.defsEl.innerHTML += sel.toString();
|
||||||
}
|
}
|
||||||
this.canvas.appendChild(defsEl);
|
this.canvas.appendChild(this.defsEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnnotation(category_id) {
|
requestAnnotation(category_id) {
|
||||||
let r = new Request(`/annotation.json?category=${category_id}&normalise=100`);
|
let r = new Request(`/annotation.json?category=${category_id}&normalise=100`);
|
||||||
fetch(r)
|
return fetch(r)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(annotation => {
|
.then(annotation => {
|
||||||
this.addAnnotationAsShape(annotation);
|
this.addAnnotationAsShape(annotation);
|
||||||
}).catch(function(e){
|
}).catch(function(e){
|
||||||
console.error(e);
|
console.error(e);
|
||||||
});;
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pointsToD(points) {
|
pointsToD(points) {
|
||||||
|
@ -187,14 +224,15 @@ class CocoCanvas {
|
||||||
'transform': `translate(${x}, ${y})`,
|
'transform': `translate(${x}, ${y})`,
|
||||||
'on': {
|
'on': {
|
||||||
'mousedown': function(downE) {
|
'mousedown': function(downE) {
|
||||||
console.log(downE);
|
// after clicking the element should be the top element
|
||||||
console.log(this)
|
// and the last svg element is drawn on top.
|
||||||
|
annEl.parentNode.appendChild(annEl);
|
||||||
|
// console.log(downE);
|
||||||
|
// console.log(this)
|
||||||
let offset = this.getMousePosition(downE);
|
let offset = this.getMousePosition(downE);
|
||||||
// offset.x -= parseFloat(downE.target.getAttributeNS(null, "x"));
|
|
||||||
// offset.y -= parseFloat(downE.target.getAttributeNS(null, "y"));
|
|
||||||
|
|
||||||
// Get initial translation amount
|
// Get initial translation amount
|
||||||
console.log(annEl.transform.baseVal);
|
// console.log(annEl.transform.baseVal);
|
||||||
let transform = annEl.transform.baseVal.getItem(0);
|
let transform = annEl.transform.baseVal.getItem(0);
|
||||||
offset.x -= transform.matrix.e;
|
offset.x -= transform.matrix.e;
|
||||||
offset.y -= transform.matrix.f;
|
offset.y -= transform.matrix.f;
|
||||||
|
@ -225,10 +263,112 @@ class CocoCanvas {
|
||||||
});
|
});
|
||||||
annEl.appendChild(pathEl);
|
annEl.appendChild(pathEl);
|
||||||
}
|
}
|
||||||
console.log(annEl);
|
|
||||||
this.canvas.appendChild(annEl);
|
this.canvas.appendChild(annEl);
|
||||||
|
|
||||||
|
// keep info on annotation elements in CocoCanvas object
|
||||||
|
annotation['element'] = annEl;
|
||||||
|
this.annotations.push(annotation);
|
||||||
|
this.savedBtnEl.disabled = false;
|
||||||
|
|
||||||
|
// based on status of checkboxes, add label or image (bit of a later hack)
|
||||||
|
this.toggleImages();
|
||||||
|
this.toggleLabels();
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert annotation shapes on canvas to images, which are masked by the shape
|
||||||
|
convertToImages() {
|
||||||
|
console.log('convert');
|
||||||
|
for(let annotation of this.annotations) {
|
||||||
|
console.log(annotation);
|
||||||
|
if(annotation['element'].getElementsByTagName('image').length) {
|
||||||
|
//already done
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let scale = annotation['scale'];
|
||||||
|
|
||||||
|
let bbox = annotation['is_normalised'] ? annotation['bbox_original'] : annotation['bbox'];
|
||||||
|
let imgEl = crel(document.createElementNS("http://www.w3.org/2000/svg", 'image'), {
|
||||||
|
'href': annotation['image']['coco_url'],
|
||||||
|
'width': annotation['image']['width']* scale,
|
||||||
|
'height': annotation['image']['height']* scale,
|
||||||
|
'x': bbox[0] * -1 * scale,
|
||||||
|
'y': bbox[1] * -1 * scale,
|
||||||
|
});
|
||||||
|
annotation['element'].prepend(imgEl );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addLabels() {
|
||||||
|
console.log('labels');
|
||||||
|
for(let annotation of this.annotations) {
|
||||||
|
console.log(annotation);
|
||||||
|
if(annotation['element'].getElementsByTagName('text').length) {
|
||||||
|
//already done
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let textEl = crel(document.createElementNS("http://www.w3.org/2000/svg", 'text'), {
|
||||||
|
'x': annotation['bbox'][2] + 5,
|
||||||
|
'y': annotation['bbox'][3],
|
||||||
|
}, categoryMap[annotation['category_id']]['name']);
|
||||||
|
annotation['element'].append(textEl );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleImages(e) {
|
||||||
|
if(this.loadImagesBtnEl.checked) {
|
||||||
|
this.convertToImages();
|
||||||
|
document.body.classList.remove('hideImages');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.body.classList.add('hideImages');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
toggleLabels(e) {
|
||||||
|
if(this.loadLabelsBtnEl.checked) {
|
||||||
|
this.addLabels();
|
||||||
|
document.body.classList.remove('hideLabels');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.body.classList.add('hideLabels');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
let scene = this.sceneSelectEl.value;
|
||||||
|
let annotations = [];
|
||||||
|
for (let ann of this.annotations) {
|
||||||
|
annotations.push({
|
||||||
|
'id': ann['id'],
|
||||||
|
'x': ann.element.transform.baseVal.getItem(0).matrix.e,
|
||||||
|
'y': ann.element.transform.baseVal.getItem(0).matrix.f
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = JSON.stringify({
|
||||||
|
'scene': this.sceneSelectEl.value,
|
||||||
|
'annotations': annotations
|
||||||
|
});
|
||||||
|
let r = new Request('/save', {'method': 'POST', 'body': data});
|
||||||
|
fetch(r)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(submission => {
|
||||||
|
// alert("Something went wrong when saving the file");
|
||||||
|
// todo redirect to submission
|
||||||
|
console.log('saved', submission);
|
||||||
|
window.location = window.location + 'saved';
|
||||||
|
}).catch(function(e){
|
||||||
|
alert("Something went wrong when saving the file");
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let cc = new CocoCanvas();
|
let cc = new CocoCanvas();
|
||||||
cc.start();
|
cc.start();
|
||||||
|
|
||||||
|
//for testing only:
|
||||||
|
//cc.requestAnnotation(1).then((e) => cc.convertToImages());
|
||||||
|
|
|
@ -1,43 +1,12 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset='utf-8'>
|
<meta charset='utf-8'>
|
||||||
<style type='text/css'>
|
<link rel="stylesheet" type="text/css" href="coco.css">
|
||||||
body{
|
|
||||||
font-family:sans-serif;
|
|
||||||
background: darkblue;
|
|
||||||
margin:0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#svgCanvas{
|
|
||||||
width:100vw;
|
|
||||||
height:100vh;
|
|
||||||
}
|
|
||||||
#catNav{
|
|
||||||
position:absolute;
|
|
||||||
top:0;
|
|
||||||
left:0;
|
|
||||||
color:white;
|
|
||||||
}
|
|
||||||
#catNav ul{
|
|
||||||
list-style:none;
|
|
||||||
padding:10px;
|
|
||||||
}
|
|
||||||
#catNav li:hover{
|
|
||||||
cursor:pointer;
|
|
||||||
text-decoration:underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
g{
|
|
||||||
cursor:grab;
|
|
||||||
}
|
|
||||||
|
|
||||||
g:active{
|
|
||||||
cursor:grabbing;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav id='catNav'>
|
<nav id='catNav' class='catNav'>
|
||||||
|
</nav>
|
||||||
|
<nav id='catNav2' class='catNav'>
|
||||||
</nav>
|
</nav>
|
||||||
<svg
|
<svg
|
||||||
id='svgCanvas'
|
id='svgCanvas'
|
||||||
|
@ -47,6 +16,17 @@ cursor:grabbing;
|
||||||
viewBox="0 0 1000 1000">
|
viewBox="0 0 1000 1000">
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<div class='buttons'>
|
||||||
|
<label id='scenelabel'>Scene <select id='scene'>
|
||||||
|
<!-- <option>-</option> -->
|
||||||
|
<option value="1">View from a drone</option>
|
||||||
|
<option value="2">Self driving car</option>
|
||||||
|
<option value="3">Product placement detector</option>
|
||||||
|
</select></label>
|
||||||
|
<label id='convert'><input type='checkbox' id='loadImages'> images</label>
|
||||||
|
<label id='labelcheck'><input type='checkbox' id='loadLabels'> labels</label>
|
||||||
|
<label id='savewrap'><input type='submit' id='save' disabled='disabled' value='save'></label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- <script src="svg-inject.min.js"></script> -->
|
<!-- <script src="svg-inject.min.js"></script> -->
|
||||||
<script src="textures.js"></script>
|
<script src="textures.js"></script>
|
||||||
|
|
40
www/saved.html
Normal file
40
www/saved.html
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Plotting Data: COCO drawings</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="coco.css">
|
||||||
|
</head>
|
||||||
|
<body class='hideImages'>
|
||||||
|
<div class='buttons buttons-inline'>
|
||||||
|
<label id='convert'><input type='checkbox' id='loadImages'> images</label>
|
||||||
|
<label id='labelcheck'><input type='checkbox' id='loadLabels'> labels</label>
|
||||||
|
</div>
|
||||||
|
<div id='saved'>
|
||||||
|
{images}
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
let loadImagesEl = document.getElementById('loadImages');
|
||||||
|
let loadLabelsEl = document.getElementById('loadLabels');
|
||||||
|
|
||||||
|
let setImageClass = function() {
|
||||||
|
if(loadImagesEl.checked) {
|
||||||
|
document.body.classList.remove('hideImages');
|
||||||
|
} else {
|
||||||
|
document.body.classList.add('hideImages');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let setLabelClass = function() {
|
||||||
|
if(loadLabelsEl.checked) {
|
||||||
|
document.body.classList.remove('hideLabels');
|
||||||
|
} else {
|
||||||
|
document.body.classList.add('hideLabels');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadImagesEl.addEventListener('change', (e) => setImageClass());
|
||||||
|
loadLabelsEl.addEventListener('change', (e) => setLabelClass());
|
||||||
|
setImageClass();
|
||||||
|
setLabelClass();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
2
www/saved/.gitignore
vendored
Normal file
2
www/saved/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
Loading…
Reference in a new issue