diff --git a/README.md b/README.md index e69de29..9ebc05c 100644 --- a/README.md +++ b/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 \ No newline at end of file diff --git a/clitools/README.md b/clitools/README.md index b69e4a5..e829f78 100644 --- a/clitools/README.md +++ b/clitools/README.md @@ -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. diff --git a/clitools/exports/.DS_Store b/clitools/exports/.DS_Store index 397ae06..8a69234 100644 Binary files a/clitools/exports/.DS_Store and b/clitools/exports/.DS_Store differ diff --git a/clitools/exports/toRedis.py b/clitools/exports/toRedis.py index 36381b8..338d1ba 100644 --- a/clitools/exports/toRedis.py +++ b/clitools/exports/toRedis.py @@ -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() diff --git a/clitools/exports/tonano.py b/clitools/exports/tonano.py index bea3924..8ff9fca 100644 --- a/clitools/exports/tonano.py +++ b/clitools/exports/tonano.py @@ -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 diff --git a/clitools/filters/.DS_Store b/clitools/filters/.DS_Store index d9b12aa..5008ddf 100644 Binary files a/clitools/filters/.DS_Store and b/clitools/filters/.DS_Store differ diff --git a/clitools/generators/.DS_Store b/clitools/generators/.DS_Store index 58a8651..813caed 100644 Binary files a/clitools/generators/.DS_Store and b/clitools/generators/.DS_Store differ diff --git a/clitools/generators/fromildb.py b/clitools/generators/fromildb.py new file mode 100644 index 0000000..43aa78d --- /dev/null +++ b/clitools/generators/fromildb.py @@ -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 ("" % + (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 "" % ( + # 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() +''' diff --git a/clitools/generators/text.py b/clitools/generators/text.py index 63fbcbd..c1bffdf 100644 --- a/clitools/generators/text.py +++ b/clitools/generators/text.py @@ -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); diff --git a/nano.py b/nano.py index c144e1f..5c6d43f 100644 --- a/nano.py +++ b/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() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..61d0e4d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +websocket-client>=1.3.3 +websocket-server>=0.4 +redis>=4.3.4 +pyOSC3>=0.0.7 diff --git a/www/simulocal.html b/www/simulocal.html index ec425c3..4f7c6ea 100644 --- a/www/simulocal.html +++ b/www/simulocal.html @@ -1,7 +1,7 @@ - Simu Rack + LJnano @@ -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);