Better docs
This commit is contained in:
parent
356755c486
commit
cd93efa04f
129
README.md
129
README.md
@ -0,0 +1,129 @@
|
||||
# LJnano
|
||||
|
||||
A lightweight, stripped-down version of LJ specifically designed to run the chain of clitools (generator + filters + exports) for pointlist processing.
|
||||
|
||||
## Overview
|
||||
|
||||
LJnano provides a local laser simulator that connects to a browser interface via WebSockets. It's designed to be minimal and focused on running the bundled clitools chain efficiently. LJnano comes with its own set of clitools in the `clitools/` directory, which includes generators, filters, and exporters for processing point lists.
|
||||
|
||||
## Installation
|
||||
|
||||
### Requirements
|
||||
|
||||
LJnano requires the following Python packages:
|
||||
|
||||
- websocket-client: For WebSocket client functionality
|
||||
- websocket-server: For WebSocket server functionality
|
||||
- redis: For Redis database interaction
|
||||
- pyOSC3: For OSC protocol support (used by some generators)
|
||||
|
||||
### Quick Install
|
||||
|
||||
A `requirements.txt` file is provided for easy installation of all dependencies:
|
||||
|
||||
```bash
|
||||
pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
### Manual Install
|
||||
|
||||
Alternatively, you can install dependencies individually:
|
||||
|
||||
```bash
|
||||
pip3 install websocket-client websocket-server redis pyOSC3
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Starting the Server
|
||||
|
||||
```bash
|
||||
python3 nano.py
|
||||
```
|
||||
|
||||
Options:
|
||||
- `-v, --verbose`: Enable verbose output
|
||||
- `-s, --server`: WS server IP (default: 127.0.0.1)
|
||||
- `-p, --port`: WS port to bind to (default: 9001)
|
||||
- `-k, --key`: Redis key to update
|
||||
|
||||
### Browser Interface
|
||||
|
||||
Open `www/simulocal.html` in a browser to view the laser simulation.
|
||||
|
||||
### Running the Clitools Chain
|
||||
|
||||
LJnano is designed to work with its bundled clitools chain located in the `clitools/` directory:
|
||||
|
||||
1. **Generators** (`clitools/generators/`): Create point lists
|
||||
2. **Filters** (`clitools/filters/`): Process and modify point lists
|
||||
3. **Exporters** (`clitools/exports/`): Output point lists to various formats
|
||||
|
||||
These tools can be chained together using Unix pipes. For example:
|
||||
|
||||
```bash
|
||||
python3 clitools/generators/dummy.py | python3 clitools/filters/kaleidoscope.py | python3 clitools/exports/tonano.py
|
||||
```
|
||||
|
||||
For detailed chain operations and examples, see `clitools/README.md`
|
||||
|
||||
### Using LJnano Output in Browser
|
||||
|
||||
To use LJnano output in a browser, use the `tonano.py` exporter located in `clitools/exports/`:
|
||||
|
||||
```bash
|
||||
# Example usage of tonano.py exporter
|
||||
python3 clitools/exports/tonano.py
|
||||
```
|
||||
|
||||
Options for tonano.py:
|
||||
- `-v, --verbose`: Enable verbose output
|
||||
- `-s, --server`: WS server IP (default: 127.0.0.1)
|
||||
- `-p, --port`: WS port to bind to (default: 9001)
|
||||
- `-k, --key`: Redis key to update (default: /pl/0/0)
|
||||
- `-o, --old`: Use old school 0-800 coordinate space
|
||||
|
||||
## Architecture
|
||||
|
||||
LJnano uses WebSockets on port 9001 by default to communicate between the server and the browser interface. The system allows for real-time visualization of laser point lists.
|
||||
|
||||
## Changelog
|
||||
|
||||
### v0.1b (Current Version)
|
||||
|
||||
#### Core Features
|
||||
- WebSocket server on port 9001 for real-time communication
|
||||
- Browser-based visualization interface (www/simulocal.html)
|
||||
- Redis integration for storing and retrieving point lists
|
||||
- Support for multiple laser simulations
|
||||
- Status indicators for laser state and connections
|
||||
|
||||
#### Bundled Clitools
|
||||
|
||||
**Generators** (in `clitools/generators/`):
|
||||
- dummy.py: Basic point list generator
|
||||
- audio.py: Audio-reactive point generation
|
||||
- turtle1.py: Turtle graphics based generator
|
||||
- blank.py: Empty template for creating new generators
|
||||
- audiogen3.py: Advanced audio-reactive generator
|
||||
- Support for NetLogo integration via file-based input
|
||||
|
||||
**Filters** (in `clitools/filters/`):
|
||||
- kaleidoscope.py: Mirrors points based on a pivot
|
||||
- anaglyph.py: Creates 3D anaglyph effects
|
||||
- colorcycle.py: Cycles through colors for points
|
||||
- redilysis.py: Redis-based analysis and modification
|
||||
|
||||
**Exporters** (in `clitools/exports/`):
|
||||
- tonano.py: Sends point lists to LJnano for visualization
|
||||
- tonano800.py: Sends point lists in 0-800 coordinate space
|
||||
- toRedis.py: Exports point lists to Redis
|
||||
- toUDP.py: Sends point lists via UDP
|
||||
- toWS.py: Sends point lists via WebSockets
|
||||
- toNull.py: Discards point lists (for testing)
|
||||
|
||||
#### Browser Interface
|
||||
- Real-time point list visualization
|
||||
- Status indicators for connections
|
||||
- Support for multiple laser displays
|
||||
- Canvas-based rendering of laser points
|
@ -9,7 +9,7 @@ BOOM | WIIIIIZ :: PHHHHHRACKRACKRACK ~~ WOOP ~~###~~ WIIT
|
||||
|
||||
## The basic loop
|
||||
```
|
||||
python3 generators/dummy.py -f 2 | python3 filters/kaleidoscope.py | python3 exports/toRedis.py -v
|
||||
python3 generators/dummy.py -f 2 | python3 filters/kaleidoscope.py | python3 exports/tonano.py -v
|
||||
------------------------------ --------------------- -------------------
|
||||
\/ \/ \/
|
||||
Generator Filter Export
|
||||
@ -55,7 +55,11 @@ These do listen and read on STDIN and do the same print'n'flush on STDOUT.
|
||||
|
||||
### Export
|
||||
|
||||
Read from STDIN and send to redis mostly
|
||||
Read from STDIN and send to LJnano.
|
||||
|
||||
* tonano.py
|
||||
|
||||
When your chain is ready and tested with LJnano going with real lasers needs LJ running and you simply change the export with :
|
||||
|
||||
* toRedis.py : provide a key, server IP, etc.
|
||||
|
||||
|
BIN
clitools/exports/.DS_Store
vendored
BIN
clitools/exports/.DS_Store
vendored
Binary file not shown.
@ -26,7 +26,7 @@ import time
|
||||
argsparser = argparse.ArgumentParser(description="Redis exporter LJ")
|
||||
argsparser.add_argument("-i","--ip",help="IP address of the Redis server ",default="127.0.0.1",type=str)
|
||||
argsparser.add_argument("-p","--port",help="Port of the Redis server ",default="6379",type=str)
|
||||
argsparser.add_argument("-k","--key",help="Redis key to update",default="0",type=str)
|
||||
argsparser.add_argument("-k","--key",help="Redis key to update",default="/pl/0/0",type=str)
|
||||
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose")
|
||||
args = argsparser.parse_args()
|
||||
|
||||
|
@ -4,9 +4,20 @@
|
||||
|
||||
'''
|
||||
tonano
|
||||
input space for X & Y : -1500,+1500
|
||||
exporter to LJ nano
|
||||
v0.1b
|
||||
|
||||
a la place de ast.literal_eval(line) : ?
|
||||
|
||||
>>> a = "[[111.121, 45.8783, 0.0],[110.936, 44.8368, 0.0],[374.849, 673.228, 230.536]]"
|
||||
>>> import json
|
||||
>>> b = json.loads(a)
|
||||
>>> b
|
||||
[[111.121, 45.8783, 0.0], [110.936, 44.8368, 0.0], [374.849, 673.228, 230.536]]
|
||||
>>> b[0]
|
||||
[111.121, 45.8783, 0.0]
|
||||
|
||||
'''
|
||||
from __future__ import print_function
|
||||
import websocket
|
||||
@ -17,6 +28,7 @@ import sys
|
||||
import random
|
||||
from websocket_server import WebsocketServer
|
||||
from socket import *
|
||||
#import ast
|
||||
|
||||
try:
|
||||
import thread
|
||||
@ -35,7 +47,8 @@ argsparser = argparse.ArgumentParser(description="tonano v0.1b help mode")
|
||||
argsparser.add_argument("-v","--verbose",action="store_true",help="Verbose output")
|
||||
argsparser.add_argument("-s","--server",help="WS server IP (127.0.0.1 by default)", type=str)
|
||||
argsparser.add_argument("-p","--port",help="WS port to bind to (9001 by default)", type=str)
|
||||
argsparser.add_argument("-k","--key",help="Redis key to update",default="0",type=str)
|
||||
argsparser.add_argument("-k","--key",help="Redis key to update",default="/pl/0/0",type=str)
|
||||
argsparser.add_argument("-o","--old",help="Coordinates in old school 0-800 space",action="store_true")
|
||||
args = argsparser.parse_args()
|
||||
|
||||
key = args.key
|
||||
@ -55,6 +68,15 @@ if args.port:
|
||||
else:
|
||||
wsPORT = 9001
|
||||
|
||||
if args.old:
|
||||
inspace = [0,800]
|
||||
else:
|
||||
inspace = [-1500,1500]
|
||||
|
||||
|
||||
outspace = [-1500,1500]
|
||||
zoom = (outspace[1]-outspace[0])/(inspace[1]-inspace[0])
|
||||
|
||||
debug("")
|
||||
debug("tonano v0.1b")
|
||||
|
||||
@ -74,7 +96,7 @@ def sendbroadcast():
|
||||
cs = socket(AF_INET, SOCK_DGRAM)
|
||||
cs.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
|
||||
cs.setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
|
||||
cs.sendto("LJ tonano 0.1".encode(), ("255.255.255.255", 54545))
|
||||
cs.sendto("LJ tonano v0.1".encode(), ("255.255.255.255", 54545))
|
||||
|
||||
|
||||
#
|
||||
@ -109,10 +131,18 @@ def on_open(ws):
|
||||
line = line.replace("]",')')
|
||||
#debug(line)
|
||||
line = "[{}]".format(line)
|
||||
|
||||
if zoom != 1.0:
|
||||
shape = []
|
||||
pointsList = ast.literal_eval(line)
|
||||
for point in pointsList:
|
||||
shape.append(((point[0]*zoom)+outspace[0],(point[1]*zoom)+outspace[0], point[2]))
|
||||
line = str(shape)
|
||||
|
||||
debug("CLI proccess sending : /simul" +" "+ line)
|
||||
#sendWSall("/simul" +" "+ str(points[laserid].decode('ascii')))
|
||||
ws.send("/simul "+line)
|
||||
#debug("exports::tosimuCLIent "+str(key)+" "+line)
|
||||
|
||||
|
||||
except EOFError:
|
||||
debug("tonano break")# no more information
|
||||
|
BIN
clitools/filters/.DS_Store
vendored
BIN
clitools/filters/.DS_Store
vendored
Binary file not shown.
BIN
clitools/generators/.DS_Store
vendored
BIN
clitools/generators/.DS_Store
vendored
Binary file not shown.
359
clitools/generators/fromildb.py
Normal file
359
clitools/generators/fromildb.py
Normal file
@ -0,0 +1,359 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# -*- mode: Python -*-
|
||||
|
||||
'''
|
||||
|
||||
fromild
|
||||
v0.1.0
|
||||
|
||||
Read/display once an .ild animation file and quit ??
|
||||
LICENCE : CC
|
||||
|
||||
by cocoa and Sam Neurohack
|
||||
|
||||
Heavy u-se of :
|
||||
|
||||
ILDA.py
|
||||
|
||||
Python module for dealing with the ILDA Image Data Transfer Format,
|
||||
an interchange format for laser image frames.
|
||||
|
||||
Copyright (c) 2008 Micah Dowty
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
'''
|
||||
|
||||
from __future__ import print_function
|
||||
import time
|
||||
import struct
|
||||
import argparse
|
||||
import sys
|
||||
import random
|
||||
|
||||
name="generator::fromild"
|
||||
|
||||
|
||||
def debug(*args, **kwargs):
|
||||
if( verbose == False ):
|
||||
return
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
||||
argsparser = argparse.ArgumentParser(description=".ild 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("-i","--ild",help=".ild file",default="book2.ild",type=str)
|
||||
args = argsparser.parse_args()
|
||||
|
||||
fps=args.fps
|
||||
verbose=args.verbose
|
||||
|
||||
optimal_looptime = 1 / fps
|
||||
debug(name+" optimal looptime "+str(optimal_looptime))
|
||||
|
||||
# Format codes
|
||||
FORMAT_3D = 0
|
||||
FORMAT_2D = 1
|
||||
FORMAT_COLOR_TABLE = 2
|
||||
|
||||
# Mapping from FORMAT_* codes to struct format strings
|
||||
formatTable = (
|
||||
'>hhhH',
|
||||
'>hhH',
|
||||
'>BBB',
|
||||
)
|
||||
|
||||
# Header values
|
||||
HEADER_MAGIC = b"ILDA\0\0\0"
|
||||
HEADER_RESERVED = 0
|
||||
HEADER_FORMAT = ">7sB16sHHHBB"
|
||||
HEADER_LEN = struct.calcsize(HEADER_FORMAT)
|
||||
|
||||
|
||||
class Table(object):
|
||||
"""Container object for one ILDA table: either a frame (table of points)
|
||||
or a palette (table of colors).
|
||||
|
||||
The 'items' list contains the data within this table. Each item
|
||||
is a tuple, corresponding to the raw values within that row of the
|
||||
table.
|
||||
|
||||
2D frame: (x, y, status)
|
||||
3D frame: (x, y, z, status)
|
||||
Color: (r, g, b)
|
||||
|
||||
"""
|
||||
def __init__(self, format=FORMAT_2D, name="",
|
||||
length=0, number=0, total=0, scanHead=0):
|
||||
self.__dict__.update(locals())
|
||||
self.items = []
|
||||
self.itemsproducer = None
|
||||
|
||||
def __repr__(self):
|
||||
return ("<ILDA.Table format=%d name=%r "
|
||||
"length=%d number=%d total=%d scanHead=%d>" %
|
||||
(self.format, self.name, self.length, self.number,
|
||||
self.total, self.scanHead))
|
||||
|
||||
def unpackHeader(self, data):
|
||||
magic, self.format, self.name, self.length, \
|
||||
self.number, self.total, self.scanHead, \
|
||||
reserved = struct.unpack(HEADER_FORMAT, data)
|
||||
print(magic, HEADER_MAGIC)
|
||||
if magic != HEADER_MAGIC:
|
||||
raise ValueError("Bad ILDA header magic. Not an ILDA file?")
|
||||
if reserved != HEADER_RESERVED:
|
||||
raise ValueError("Reserved ILDA field is not zero.")
|
||||
|
||||
def packHeader(self):
|
||||
return struct.pack(HEADER_FORMAT, HEADER_MAGIC, self.format,
|
||||
self.name, self.length, self.number,
|
||||
self.total, self.scanHead, HEADER_RESERVED)
|
||||
|
||||
def readHeader(self, stream):
|
||||
self.unpackHeader(stream.read(HEADER_LEN))
|
||||
|
||||
def writeHeader(self, stream):
|
||||
stream.write(self.packHeader())
|
||||
|
||||
def _getItemFormat(self):
|
||||
try:
|
||||
return formatTable[self.format]
|
||||
except IndexError:
|
||||
raise ValueError("Unsupported format code")
|
||||
|
||||
def read_stream(self, stream):
|
||||
"""Read the header, then read all items in this table."""
|
||||
self.readHeader(stream)
|
||||
if self.length:
|
||||
fmt = self._getItemFormat()
|
||||
itemSize = struct.calcsize(fmt)
|
||||
self.items = [struct.unpack(fmt, stream.read(itemSize))
|
||||
for i in range(self.length)]
|
||||
self.itemsproducer = self.produce()
|
||||
|
||||
def write(self, stream):
|
||||
"""Write the header, then write all items in this table."""
|
||||
self.writeHeader(stream)
|
||||
if self.length:
|
||||
fmt = self._getItemFormat()
|
||||
itemSize = struct.calcsize(fmt)
|
||||
stream.write(''.join([struct.pack(fmt, *item)
|
||||
for item in self.items]))
|
||||
|
||||
def iterPoints(self):
|
||||
"""Iterate over Point instances for each item in this table.
|
||||
Only makes sense if this is a 2D or 3D point table.
|
||||
"""
|
||||
for item in self.items:
|
||||
p = Point()
|
||||
p.decode(item)
|
||||
yield p
|
||||
|
||||
|
||||
def produce(self):
|
||||
"""Iterate over Point instances for each item in this table.
|
||||
Only makes sense if this is a 2D or 3D point table.
|
||||
"""
|
||||
while True:
|
||||
for item in self.items:
|
||||
p = Point()
|
||||
p.decode(item)
|
||||
yield p.encode()
|
||||
#yield (p.x, p.y, p.z, p.color, p.blanking)
|
||||
|
||||
def read(self, cap):
|
||||
"""yields what dac.play_stream() needs (x, y, z, ?, ?)
|
||||
"""
|
||||
return [next(self.itemsproducer) for i in range(cap)]
|
||||
|
||||
|
||||
class Point:
|
||||
"""Abstraction for one vector point. The Table object, for
|
||||
completeness and efficiency, stores raw tuples for each
|
||||
point. This is a higher level interface that decodes the status
|
||||
bits and represents coordinates in floating point.
|
||||
"""
|
||||
def __init__(self, x=0.0, y=0.0, z=0.0, color=0, blanking=False):
|
||||
self.__dict__.update(locals())
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
return "%s, %s, %s, %s, %s" % (
|
||||
self.x, self.y, self.z, self.color, self.blanking)
|
||||
#return "<ILDA.Point (%s, %s, %s) color=%s blanking=%s>" % (
|
||||
# self.x, self.y, self.z, self.color, self.blanking)
|
||||
|
||||
def encode(self):
|
||||
status = self.color & 0xFF
|
||||
if self.blanking:
|
||||
status |= 1 << 14
|
||||
|
||||
return (
|
||||
int( min(0x7FFF, max(-0x7FFF, self.x * 0x7FFF)) ),
|
||||
int( min(0x7FFF, max(-0x7FFF, self.y * 0x7FFF)) ),
|
||||
int( min(0x7FFF, max(-0x7FFF, self.z * 0x7FFF)) ),
|
||||
int( min(0x7FFF, max(-0x7FFF, self.color * 0x7FFF)) ),
|
||||
int( min(0x7FFF, max(-0x7FFF, self.blanking * 0x7FFF)) )
|
||||
)
|
||||
|
||||
def decode(self, t):
|
||||
#print "~~ Decoding, t of len "+ str(len(t)) +" is: " + str(t)
|
||||
self.x = t[0] / 0x7FFF
|
||||
self.y = t[1] / 0x7FFF
|
||||
if len(t) > 3:
|
||||
self.z = t[2] / 0x7FFF
|
||||
# self.color = t[3] & 0xFF
|
||||
# self.blanking = (t[3] & (1 << 14)) != 0
|
||||
else:
|
||||
self.z = 0.0
|
||||
|
||||
self.color = t[-1] & 0xFF
|
||||
self.blanking = (t[-1] & (1 << 14)) != 0
|
||||
|
||||
|
||||
def read(stream):
|
||||
"""Read ILDA data from a stream until we hit the
|
||||
end-of-stream marker. Yields a sequence of Table objects.
|
||||
"""
|
||||
while True:
|
||||
t = Table()
|
||||
t.read_stream(stream)
|
||||
if not t.length:
|
||||
# End-of-stream
|
||||
break
|
||||
yield t
|
||||
|
||||
|
||||
def write(stream, tables):
|
||||
"""Write a sequence of tables in ILDA format,
|
||||
terminated by an end-of-stream marker.
|
||||
"""
|
||||
for t in tables:
|
||||
t.write(stream)
|
||||
Table().write(stream)
|
||||
|
||||
|
||||
def readFrames(stream):
|
||||
"""Read ILDA data from a stream, and ignore
|
||||
all non-frame tables. Yields only 2D or 3D
|
||||
point tables.
|
||||
"""
|
||||
for t in read(stream):
|
||||
if t.format in (FORMAT_2D, FORMAT_3D):
|
||||
yield t
|
||||
|
||||
|
||||
def readFirstFrame(stream):
|
||||
"""Read only a single frame from an ILDA stream."""
|
||||
for frame in readFrames(stream):
|
||||
return frame
|
||||
|
||||
'''
|
||||
|
||||
#LD = LaserDisplay()
|
||||
LD = LaserDisplay({"server":"localhost","port": 50000})
|
||||
LD.set_scan_rate(37000)
|
||||
LD.set_blanking_delay(0)
|
||||
'''
|
||||
|
||||
WIDTH=700
|
||||
HEIGHT=700
|
||||
|
||||
ilda_file = open(args.ild, 'rb')
|
||||
ilda_frames = readFrames(ilda_file)
|
||||
|
||||
frames = []
|
||||
|
||||
for myframe in ilda_frames:
|
||||
frame = []
|
||||
debug("Frame", myframe.number, "/",myframe.total, "length", myframe.length)
|
||||
for mypoint in myframe.iterPoints():
|
||||
frame.append([WIDTH/2 + (WIDTH/2)*mypoint.x, HEIGHT/2 + (HEIGHT/2)*mypoint.y])
|
||||
#debug(frame)
|
||||
frames.append(frame)
|
||||
if myframe.number +1 == myframe.total:
|
||||
debug("last frame", myframe.number, myframe.total)
|
||||
break
|
||||
|
||||
|
||||
ilda_file.close()
|
||||
debug(len(frames))
|
||||
#debug(frame)
|
||||
#LD.set_color(YELLOW)
|
||||
'''
|
||||
for frame in frames:
|
||||
for _ in range(2):
|
||||
shape =[]
|
||||
for point in frame:
|
||||
#LD.set_color(p.color)
|
||||
if random.random()<=0.5:
|
||||
shape.append([point[0], point[1],0])
|
||||
#debug(shape)
|
||||
#shape =[]
|
||||
'''
|
||||
'''
|
||||
while True:
|
||||
LD.messageBuffer = m
|
||||
LD.show_frame()
|
||||
|
||||
|
||||
|
||||
f = open(args.ild, 'rb')
|
||||
myframe = readFirstFrame(f)
|
||||
|
||||
while myframe.number +1< myframe.total:
|
||||
|
||||
start = time.time()
|
||||
shape =[]
|
||||
|
||||
if myframe is None:
|
||||
f.close()
|
||||
sys.exit() # does not quit ????
|
||||
|
||||
debug(name,"Frame", myframe.number, "/",myframe.total, "length", myframe.length)
|
||||
|
||||
for p in myframe.iterPoints():
|
||||
p2 = str(p)
|
||||
point = p2.split(',')
|
||||
|
||||
x = float(point[0])
|
||||
y = float(point[1])
|
||||
z = float(point[2])
|
||||
color = int(point[3])
|
||||
blanking = point[4][1:]
|
||||
|
||||
if blanking == "True":
|
||||
shape.append([300+(x*300),300+(-y*300),0])
|
||||
else:
|
||||
shape.append([300+(x*300),300+(-y*300),65535])
|
||||
|
||||
print(shape, flush=True);
|
||||
#debug(shape)
|
||||
myframe = readFirstFrame(f)
|
||||
looptime = time.time() - start
|
||||
if( looptime < optimal_looptime ):
|
||||
time.sleep( optimal_looptime - looptime)
|
||||
debug(name+" micro sleep:"+str( optimal_looptime - looptime))
|
||||
|
||||
f.close()
|
||||
'''
|
@ -32,6 +32,7 @@ from HersheyFonts import HersheyFonts
|
||||
|
||||
name="generator::text"
|
||||
|
||||
Position = [-1500,0]
|
||||
|
||||
def debug(*args, **kwargs):
|
||||
if( verbose == False ):
|
||||
@ -42,7 +43,7 @@ def debug(*args, **kwargs):
|
||||
argsparser = argparse.ArgumentParser(description="Text 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("-t","--text",help="Text to display",default="hello",type=str)
|
||||
argsparser.add_argument("-t","--text",help="Text to display",default="proton",type=str)
|
||||
argsparser.add_argument("-p","--police",help="Herschey font to use",default="futural",type=str)
|
||||
args = argsparser.parse_args()
|
||||
|
||||
@ -73,14 +74,18 @@ Allfonts = ['futural', 'astrology', 'cursive', 'cyrilc_1', 'cyrillic', 'futuram'
|
||||
thefont = HersheyFonts()
|
||||
#thefont.load_default_font()
|
||||
thefont.load_default_font(fontname)
|
||||
thefont.normalize_rendering(120)
|
||||
thefont.normalize_rendering(300)
|
||||
|
||||
for (x1, y1), (x2, y2) in thefont.lines_for_text(text):
|
||||
shape.append([x1, -y1+400, color])
|
||||
shape.append([x2 ,-y2+400, color])
|
||||
|
||||
shape.append([Position[0]+x1, Position[1]-y1, color])
|
||||
shape.append([Position[0]+x2, Position[1]-y2, color])
|
||||
'''
|
||||
shape.append([x1+ScreenX[0]+Position[0], -y1+ScreenY[0]+Position[1], color])
|
||||
shape.append([x2+ScreenX[0]+Position[0] ,-y2+ScreenY[0]+Position[1], color])
|
||||
'''
|
||||
while True:
|
||||
|
||||
debug(shape)
|
||||
start = time.time()
|
||||
print(shape, flush=True);
|
||||
|
||||
|
18
nano.py
18
nano.py
@ -62,11 +62,11 @@ if args.port:
|
||||
else:
|
||||
wsPORT = 9001
|
||||
|
||||
debug("")
|
||||
debug("LJnano v0.1b")
|
||||
print("")
|
||||
print("LJnano v0.1b")
|
||||
|
||||
points0 = "[(150.0, 230.0, 65280), (170.0, 170.0, 65280), (230.0, 170.0, 65280), (210.0, 230.0, 65280), (150.0, 230.0, 65280)]"
|
||||
points1 = "[(180.0, 230.0, 65280), (200.0, 170.0, 65280), (180.0, 230.0, 65280)]"
|
||||
points0 = "[(-150.0, 230.0, 65280), (-70.0, -170.0, 65280), (310.0, -170.0, 65280), (210.0, 230.0, 65280), (-150.0, 230.0, 65280)]"
|
||||
points1 = "[(200.0, 170.0, 65280), (-180.0, -230.0, 65280)]"
|
||||
points2 = "[(170.0, 190.0, 65280), (200.0, 170.0, 65280), (230.0, 190.0, 65280), (230.0, 200.0, 65280), (170.0, 230.0, 65280), (230.0, 230.0, 65280)]"
|
||||
points3 = "[(170.0, 170.0, 65280), (200.0, 170.0, 65280), (230.0, 190.0, 65280), (200.0, 200.0, 65280), (230.0, 210.0, 65280), (200.0, 230.0, 65280), (170.0, 230.0, 65280)]"
|
||||
points = [points0, points1, points2, points3]
|
||||
@ -157,7 +157,6 @@ def sendbroadcast():
|
||||
# Websocket server
|
||||
#
|
||||
|
||||
|
||||
def client_list():
|
||||
|
||||
clients = []
|
||||
@ -171,7 +170,7 @@ def new_client(client, wserver):
|
||||
|
||||
debug("WS server got new client connected and was given id %d" % client['id'])
|
||||
toKey('WSclients', client_list())
|
||||
sendWSall("/status Hello " + str(client['id']))
|
||||
sendWSall("/status nano:Hello " + str(client['id']))
|
||||
sendWSall("/laser "+str(0))
|
||||
sendWSall("/lack/" + str(0) + " 3")
|
||||
sendWSall("/lstt/" + str(0) + " 3")
|
||||
@ -237,13 +236,12 @@ def LaunchServer(*args):
|
||||
# Websocket server
|
||||
wserver = WebsocketServer(wsPORT,host=serverIP)
|
||||
|
||||
debug("Launching Websocket server...")
|
||||
debug("at", serverIP, "port",wsPORT)
|
||||
print("Launching Websocket server at", serverIP, "port",wsPORT)
|
||||
wserver.set_fn_new_client(new_client)
|
||||
wserver.set_fn_client_left(client_left)
|
||||
wserver.set_fn_message_received(message_received)
|
||||
debug("LJ local server running...")
|
||||
debug("")
|
||||
print("Nano is running. Open/reload www/simulocal.html in a browser !")
|
||||
print()
|
||||
|
||||
# websocket server loop
|
||||
wserver.run_forever()
|
||||
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
@ -0,0 +1,4 @@
|
||||
websocket-client>=1.3.3
|
||||
websocket-server>=0.4
|
||||
redis>=4.3.4
|
||||
pyOSC3>=0.0.7
|
@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Simu Rack</title>
|
||||
<title>LJnano</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="apple-mobile-web-app-title" content="LJ">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
@ -452,7 +452,7 @@
|
||||
var ctx = canvas.getContext("2d");
|
||||
var lastpoint = { x: 0, y: 0, color: 0};
|
||||
ctx.clearRect(0,0,400,400);
|
||||
var zoom = 0.5;
|
||||
var zoom = 0.1333;
|
||||
//ctx.save
|
||||
|
||||
// Draws every segment received, except black colored target ones
|
||||
@ -461,18 +461,19 @@
|
||||
{
|
||||
ctx.clearRect(0,0,400,400);
|
||||
lastpoint = {
|
||||
x:pl2[0],
|
||||
y:pl2[1],
|
||||
x:pl2[0]+1500,
|
||||
y:pl2[1]+1500,
|
||||
color:pl2[2]
|
||||
}
|
||||
for (var i = 0; i <= pl2.length; i+=3)
|
||||
for (var i = 0; i <= pl2.length-1; i+=3)
|
||||
{
|
||||
point = {
|
||||
x:pl2[i],
|
||||
y:pl2[i+1],
|
||||
x:pl2[i]+1500,
|
||||
y:pl2[i+1]+1500,
|
||||
color:pl2[i+2]
|
||||
}
|
||||
// console.log(lastpoint,point)
|
||||
//console.log(point)
|
||||
//console.log(point.x * zoom, point.y * zoom);
|
||||
// if the target is black, skip drawing
|
||||
if( point.color != 0){
|
||||
ctx.beginPath()
|
||||
@ -480,6 +481,7 @@
|
||||
//ctx.shadowOffsetY = 0;
|
||||
ctx.shadowBlur = 5;
|
||||
ctx.shadowColor = 'rgba(255, 255, 255, 1)';
|
||||
///ctx.shadowColor = 'rgba(255, 255, 255, 1)';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = "#"+(point.color + Math.pow(16, 6)).toString(16).slice(-6);
|
||||
ctx.moveTo(lastpoint.x * zoom, lastpoint.y * zoom);
|
||||
|
Loading…
Reference in New Issue
Block a user