lj-clitools/generators/fromGML.py
2020-11-11 17:31:08 +01:00

406 lines
12 KiB
Python

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# -*- mode: Python -*-
'''
fromGML
v0.1.0
Display a GML file
See GML specs at the end.
Support the gml spec="1.0 (minimum)"
and header/client/name
and maybe one day drawing/brush/color
LICENCE : CC
by cocoa and Sam Neurohack
Heavy use of : https://github.com/kgn/pygml
'''
from __future__ import print_function
import time
import struct
import argparse
import sys
import xml.etree.ElementTree as etree
#import urllib
from datetime import datetime
import math, random
import ast
name="generator::fromgml"
def debug(*args, **kwargs):
if( verbose == False ):
return
print(*args, file=sys.stderr, **kwargs)
argsparser = argparse.ArgumentParser(description="GML file frame generator")
argsparser.add_argument("-f","--fps",help="Frame Per Second",default=30,type=int)
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
argsparser.add_argument("-g","--gml",help=".gml file",default="147.gml",type=str)
argsparser.add_argument("-t","--total",help="Total time",default=32,type=int)
argsparser.add_argument("-m","--mode",help="once or anim mode",default="anim",type=str)
argsparser.add_argument("-s","--skip",help="% of points to skip",default="0.4",type=float)
argsparser.add_argument("-r","--rot",help="(angleX, angleY, angleZ) in degree",default="(0,0,270)",type=str)
args = argsparser.parse_args()
fps=args.fps
verbose=args.verbose
mode = args.mode
optimal_looptime = 1 / fps
angles = ast.literal_eval(args.rot)
debug(name+" optimal frame time "+str(optimal_looptime))
TOTAL_TIME=float(args.total)
TIME_STRETCH = 1
ZOOM=1.0
DELTA = 7
width = 500
height = 500
centerX = width / 2
centerY = height / 2
# 3D to 2D projection parameters
fov = 200
viewer_distance = 2.2
skip = args.skip
#skip is the percentage of points that we ignore in order to render
# faster in the laser display. Unfortunately we are not able to render too
# complex content in our display without resulting in a lot of blinking.
# return a list with all points
def readGML(filename):
outputData = []
tree = etree.parse(filename)
root = tree.getroot()
'''
if (root.tag.lower() != "gml"):
print("Not a GML file.")
return
'''
#~
tag = root.find("tag")
header = tag.find("header")
if header != None:
client = header.find("client")
if client != None:
debug("Graffiti name :", client.find("name").text)
drawing = tag.find("drawing")
environment = header.find("environment")
if not environment:
environment = tag.find("environment")
#screenBounds = environment.find("screenBounds")
#globalScale = (1.0,1.0,1.0)
#dim = (float(screenBounds.find("x").text) * globalScale[0], float(screenBounds.find("y").text) * globalScale[1], float(screenBounds.find("z").text) * globalScale[2])
#dim = (40.0,40.0,40.0)
#~
strokes = drawing.findall("stroke")
for stroke in strokes:
pointsEl = stroke.findall("pt")
for pointEl in pointsEl:
x = float(pointEl.find("x").text) - 0.5
y = float(pointEl.find("y").text) - 0.5
z = float(pointEl.find("z").text) - 0.5
transpoint = Rot(x,y,z,angles[0],angles[1],angles[2])
x = (transpoint[0]*ZOOM*width/2) + (width/2)
y = (transpoint[1]*ZOOM*height/2) + (height/2)
z = transpoint[2]
# WIDTH/2 + ZOOM*point[0]*WIDTH/2, HEIGHT/2 + ZOOM*point[1]*HEIGHT/2
time = float(pointEl.find("time").text)
outputData.append([x,y,z,time])
#print(outputData)
return outputData
def Rot(x, y, z, angleX, angleY, angleZ):
rad = angleX * math.pi / 180
cosa = math.cos(rad)
sina = math.sin(rad)
y2 = y
y = y2 * cosa - z * sina
z = y2 * sina + z * cosa
rad = angleY * math.pi / 180
cosa = math.cos(rad)
sina = math.sin(rad)
z2 = z
z = z2 * cosa - x * sina
x = z2 * sina + x * cosa
rad = angleZ * math.pi / 180
cosa = math.cos(rad)
sina = math.sin(rad)
x2 = x
x = x2 * cosa - y * sina
y = x2 * sina + y * cosa
return (x,y,z)
#[x,y,z,time]
def iterPoints():
for point in gml:
yield point
# Play once during total time arg
def Once():
debug(name,"play once mode")
shape = []
for point in gml:
shape.append([point[0],point[1], 65535])
debug(name + str(shape))
t0=datetime.now()
deltat=0
while deltat<TOTAL_TIME:
start = time.time()
print(shape, flush=True);
looptime = time.time() - start
if( looptime < optimal_looptime ):
time.sleep( optimal_looptime - looptime)
debug(name+" micro sleep:"+str( optimal_looptime - looptime))
delta = datetime.now() - t0
deltat = delta.seconds + delta.microseconds/1000000.0
deltat = float(deltat)/TIME_STRETCH
# Anim
def Anim():
debug(name+" anim mode")
t0=datetime.now()
deltat = 0
while deltat<TOTAL_TIME:
delta = datetime.now() - t0
deltat = delta.seconds + delta.microseconds/1000000.0
deltat = float(deltat)/TIME_STRETCH
if deltat > TOTAL_TIME:
t0=datetime.now()
first=True
shape = []
for point in iterPoints():
if point[3] <= deltat and deltat <= point[3]+DELTA and random.random()<(1-skip):
if first:
first=False
else:
#LD.draw_point(WIDTH/2 + ZOOM*point.x*WIDTH/2, HEIGHT/2 + ZOOM*point.y*HEIGHT/2)
shape.append([point[0], point[1], 65535])
print(shape, flush=True);
debug(name + " Reading : "+args.gml+" in "+mode+" mode.")
gml = readGML(args.gml)
debug(name + " total points : "+ str(len(gml)))
if mode =="once":
Once()
else:
Anim()
debug(name + " ends.")
exit()
'''
<gml spec="1.0 (minimum)">
<tag>
<drawing>
<stroke>
<pt>
<x>0.0</x>
<y>0.0</y>
</pt>
</stroke>
</drawing>
</tag>
</gml>
<gml spec="1.0">
<tag>
<header>
<client> <!-- how, who, what and where -->
<name>Laser Tag</name> <!-- application name -->
<version>2.0</version> <!-- application version -->
<username>MyUserName</username> <!-- user name on 000000book.com, optional -->
<permalink>http://000000book.com/data/156/</permalink> <!-- URL to .gml data on 000000book.com, optional -->
<keywords>katsu,paris,2010</keywords> <!-- comma-separated -->
<uniquekey>28sks922ks992</uniquekey> <!-- iPhone uuid, MAC address, etc -->
<ip>192.168.1.1</ip>
<time>1928372722</time> <!-- unixtime -->
<location>
<lon>-39.392922</lon>
<lat>53.29292</lat>
</location>
</client>
<!-- This is all stuff that relates to the orientation and dimensions of the client -->
<!-- So that we know how to re-map the 0.0-1.0 coordinates that come in for each point -->
<!-- Also for figuring out the down vector for devices with accelerometers and how that effects drips -->
<!-- All numbers should be between 0.0 - 1.0 -->
<environment>
<offset>
<x>0.0</x>
<y>0.0</y>
<z>0.0</z>
</offset>
<rotation>
<x>0.0</x>
<y>0.0</y>
<z>0.0</z>
</rotation>
<up>
<x>0.0</x> <!-- commonly up for iphone apps -->
<y>-1.0</y> <!-- most common -->
<z>0.0</z>
</up>
<screenbounds> <!-- use this as your multipler to get 0.0 to 1.0 back to right size - pts should never go off 0.0 to 1.0 -->
<x>1024</x>
<y>768</y>
<z>0</z>
</screenbounds>
<origin>
<x>0</x>
<y>0</y>
<z>0</z>
</origin>
<realscale> <!-- how these units relate to real world units - good for laser tag -->
<x>1000</x>
<y>600</y>
<z>0</z>
<unit>cm</unit>
</realscale>
<audio>youraudio.mp3</audio> <!-- path to audio file -->
<background>yourimage.jpg</background> <!-- path to image file -->
</environment>
</header>
<drawing>
<!-- for all stroke and movement stuff it helps to have everything inside the stroke tag -->
<!-- this way it is easy to get a sense of order to events -->
<stroke isdrawing="false"> <!-- for non drawing mouse movements -->
<pt>
<x>0.0</x>
<y>0.0</y>
<z>0.0</z> <!--this is optional -->
<t>0.013</t> <!-- time is optional too -->
<!-- NOTE: older versions of GML use <time> instead of <t> -->
</pt>
</stroke>
<stroke> <!-- by default stroke drawing is true -->
<!-- each stroke could be drawn with a different brush -->
<!-- if no brush tag is found for a stroke then it inherits the previous settings -->
<brush>
<mode>0</mode> <!-- same as uniqueStyleID but an internal reference -->
<uniquestyleid>LaserTagArrowLetters</uniquestyleid> <!-- unique blackbook string for your style -->
<!-- see note about spec at the bottom - like unique style but with extra info -->
<spec>http://aurltodescribethebrushspec.com/someSpec.xml</spec>
<width>10</width>
<speedtowidthratio>1.5</speedtowidthratio> <!-- put 0 for fixed width -->
<dripamnt>1.0</dripamnt>
<dripspeed>1.0</dripspeed>
<layerabsolute>0</layerabsolute> <!--Think photoshop layers-->
<color>
<r>255</r>
<g>255</g>
<b>255</b>
<a>255</a> <!-- optional -->
</color>
<dripvecrelativetoup> <!-- what angle do our drips go in relation to our up vector -->
<x>0</x>
<y>1</y>
<z>0</z>
</dripvecrelativetoup>
</brush>
<pt>
<x>0.0</x>
<y>0.0</y>
<z>0.0</z> <!--this is optional -->
<t>0.013</t> <!-- time is optional too -->
</pt>
<pt>
<x>0.0</x>
<y>0.0</y>
<z>0.0</z> <!--this is optional -->
<t>0.023</t> <!-- time is optional too -->
</pt>
</stroke>
<!-- this stroke inherits the previous stroke properties -->
<!-- but changes color and draws on the layer below -->
<stroke>
<info> <!-- optional info - more stuff soon-->
<curved>true</curved>
</info>
<brush>
<color>
<r>255</r>
<g>255</g>
<b>0</b>
</color>
<layerrelative> <!-- this means one layer bellow the previous layer -->
-1
</layerrelative>
</brush>
<pt>
<x>0.0</x>
<y>0.0</y>
</pt>
<pt>
<x>0.0</x>
<y>0.0</y>
</pt>
</stroke>
<stroke>
<pt>
<pres>0.5</pres> <!-- Optional. Preasure range from 0 to 1 -->
<rot>0.5</rot> <!-- Optional. Rotation range from 0 to 1 for 0 to 2*PI -->
<dir> <!-- Optional Direction -->
<x></x> <!-- range from 0 to 1 -->
<y></y> <!-- range from 0 to 1 -->
<z></z> <!-- Optional inside direction. Range from 0 to 1 -->
</dir>
</pt>
</stroke>
</drawing>
</tag>
</gml>
'''