├── image.png ├── cabinet.skp ├── IMG_8115.webp ├── rpi-pinout@2x.png ├── .gitignore ├── colorknot ├── Source │ ├── pdxinfo │ ├── main.lua │ ├── ztest.lua │ ├── knot-ordered.lua │ ├── knot.lua │ └── icosahedra.lua ├── luaglue.h ├── .nova │ └── Tasks │ │ └── Playdate Simulator.json ├── mini3d │ ├── mini3d.c │ ├── mini3d.h │ ├── render.h │ ├── shape.h │ ├── 3dmath.h │ ├── shape.c │ ├── 3dmath.c │ ├── scene.h │ ├── render.c │ └── scene.c ├── License.md ├── Makefile ├── main.c ├── CMakeLists.txt ├── _lib3d.md ├── luaglue.c └── core_cmFunc.h ├── crank ├── sensor1 │ ├── fp-lib-table │ ├── sym-lib-table │ ├── sensor.kicad_prl │ └── sensor.kicad_pro ├── sensor2 │ ├── sensor2.kicad_prl │ └── sensor2.kicad_pro └── crank.ino ├── mirrorpi ├── controls.h ├── audio.h ├── serial.h ├── frame.h ├── Makefile ├── constants.h ├── ringbuffer.h ├── stream.h ├── controls.c ├── audio.c ├── ringbuffer.c ├── main.c ├── frame.c ├── serial.c └── stream.c ├── runmirror.sh ├── LICENSE ├── README.md └── resetbutton.py /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daveatpanic/PlaydateCabinet/HEAD/image.png -------------------------------------------------------------------------------- /cabinet.skp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daveatpanic/PlaydateCabinet/HEAD/cabinet.skp -------------------------------------------------------------------------------- /IMG_8115.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daveatpanic/PlaydateCabinet/HEAD/IMG_8115.webp -------------------------------------------------------------------------------- /rpi-pinout@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daveatpanic/PlaydateCabinet/HEAD/rpi-pinout@2x.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.o 3 | crank/sensor1/sensor-backups 4 | crank/sensor2/sensor2-backups 5 | -------------------------------------------------------------------------------- /colorknot/Source/pdxinfo: -------------------------------------------------------------------------------- 1 | name=3D Test in Color! 2 | author=Dave Hayden 3 | bundleID=com.panic.3dtest 4 | -------------------------------------------------------------------------------- /crank/sensor1/fp-lib-table: -------------------------------------------------------------------------------- 1 | (fp_lib_table 2 | (version 7) 3 | (lib (name "Seeeduino XIAO KICAD")(type "KiCad")(uri "/Users/dave/Downloads/Seeeduino XIAO KICAD")(options "")(descr "")) 4 | ) 5 | -------------------------------------------------------------------------------- /crank/sensor1/sym-lib-table: -------------------------------------------------------------------------------- 1 | (sym_lib_table 2 | (version 7) 3 | (lib (name "Seeeduino XIAO")(type "Legacy")(uri "/Users/dave/Downloads/Seeeduino XIAO KICAD/Seeeduino XIAO.lib")(options "")(descr "")) 4 | ) 5 | -------------------------------------------------------------------------------- /colorknot/Source/main.lua: -------------------------------------------------------------------------------- 1 | function playdate.update() end 2 | 3 | --import "ztest" 4 | --import "knot" 5 | import "knot-ordered" 6 | --import "icosahedra" 7 | 8 | function playdate.gameWillPause() 9 | resetFrame() 10 | end -------------------------------------------------------------------------------- /mirrorpi/controls.h: -------------------------------------------------------------------------------- 1 | // 2 | // controls.h 3 | // MirrorJr 4 | // 5 | // Created by Dave Hayden on 9/28/24. 6 | // 7 | 8 | #ifndef controls_h 9 | #define controls_h 10 | 11 | #include 12 | 13 | bool controls_init(); 14 | void controls_scan(); 15 | 16 | #endif /* controls_h */ 17 | -------------------------------------------------------------------------------- /colorknot/luaglue.h: -------------------------------------------------------------------------------- 1 | // 2 | // 3d-glue.h 3 | // Extension 4 | // 5 | // Created by Dave Hayden on 9/19/15. 6 | // Copyright © 2015 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #ifndef _d_glue_h 10 | #define _d_glue_h 11 | 12 | #include "pd_api.h" 13 | 14 | void register3D(PlaydateAPI* playdate); 15 | 16 | #endif /* _d_glue_h */ 17 | -------------------------------------------------------------------------------- /colorknot/.nova/Tasks/Playdate Simulator.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension" : { 3 | "identifier" : "com.panic.Playdate", 4 | "name" : "Playdate" 5 | }, 6 | "extensionTemplate" : "simulator", 7 | "extensionValues" : { 8 | "playdate.build-type" : "make", 9 | "playdate.product-name" : "3DLibrary" 10 | }, 11 | "identifier" : "98EFB50B-1584-4BE0-8131-F088F7EC329F" 12 | } 13 | -------------------------------------------------------------------------------- /colorknot/mini3d/mini3d.c: -------------------------------------------------------------------------------- 1 | // 2 | // mini3d.c 3 | // Extension 4 | // 5 | // Created by Dave Hayden on 10/20/15. 6 | // Copyright © 2015 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #include "mini3d.h" 10 | 11 | void* (*m3d_realloc)(void* ptr, size_t size); 12 | 13 | void mini3d_setRealloc(void* (*realloc)(void* ptr, size_t size)) 14 | { 15 | m3d_realloc = realloc; 16 | } 17 | -------------------------------------------------------------------------------- /mirrorpi/audio.h: -------------------------------------------------------------------------------- 1 | // 2 | // audio.h 3 | // MirrorJr 4 | // 5 | // Created by Dave Hayden on 9/25/24. 6 | // 7 | 8 | #ifndef audio_h 9 | #define audio_h 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | bool audio_init(); 16 | void audio_setFormat(unsigned int channels); 17 | 18 | void audio_stop(); 19 | 20 | void audio_addData(uint8_t* data, unsigned int len); 21 | void audio_addSilence(unsigned int len); 22 | 23 | #endif /* audio_h */ 24 | -------------------------------------------------------------------------------- /runmirror.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo python resetbutton.py & 3 | 4 | # wait for A button press (gpio 25) before starting mirror so that 5 | # we can log in if we need to 6 | 7 | # pigs needs pigpiod running.. 8 | sudo pigpiod 9 | pigs modes 25 r 10 | 11 | while true; do 12 | if [[ $(pigs r 25) == "0" ]]; then break; fi 13 | sleep 1 14 | done 15 | 16 | # ..but mirror doesn't work if it's running :eyeroll: 17 | sudo killall pigpiod 18 | 19 | # finally, loop mirror so it restart on crash/button reset 20 | while true; do sudo /home/pi/mirror; done 21 | -------------------------------------------------------------------------------- /mirrorpi/serial.h: -------------------------------------------------------------------------------- 1 | // 2 | // serial.h 3 | // MirrorJr 4 | // 5 | // Created by Dave Hayden on 9/28/24. 6 | // 7 | 8 | #ifndef serial_h 9 | #define serial_h 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | bool ser_open(); 17 | bool ser_isOpen(); 18 | void ser_close(); 19 | 20 | ssize_t ser_write(const char* buffer, size_t size); 21 | #define ser_writestr(s) ser_write(s, strlen(s)); 22 | ssize_t ser_writeLine(const char* cmd); 23 | 24 | ssize_t ser_writeNonblocking(const char* buffer, size_t size); 25 | ssize_t ser_read(uint8_t* buffer, size_t size); 26 | void ser_flush(void); 27 | 28 | #endif /* serial_h */ 29 | -------------------------------------------------------------------------------- /colorknot/License.md: -------------------------------------------------------------------------------- 1 | BSD Zero Clause License 2 | ======================= 3 | 4 | Copyright (C) Panic Inc. help@play.date 5 | 6 | Permission to use, copy, modify, and/or distribute this software for any 7 | purpose with or without fee is hereby granted. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 11 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14 | OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15 | PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /mirrorpi/frame.h: -------------------------------------------------------------------------------- 1 | // 2 | // frame.h 3 | // MirrorJr 4 | // 5 | // Created by Dave Hayden on 9/25/24. 6 | // 7 | 8 | #ifndef frame_h 9 | #define frame_h 10 | 11 | #include 12 | #include "SDL.h" 13 | 14 | #define LCD_ROWS 240 15 | #define LCD_COLUMNS 400 16 | #define LCD_ROWSIZE (LCD_COLUMNS/8) 17 | 18 | bool frame_init(SDL_Window* window); 19 | void frame_begin(uint32_t timestamp); 20 | void frame_setRow(unsigned int row, const uint8_t* data); 21 | void frame_end(); 22 | void frame_present(); 23 | void frame_showWaitScreen(const uint8_t* addr); 24 | 25 | void frame_reset(); 26 | 27 | typedef struct { uint8_t r; uint8_t g; uint8_t b; } RGB; 28 | 29 | void frame_set1BitPalette(RGB palette[2]); 30 | void frame_set4BitPalette(RGB palette[16]); 31 | 32 | #endif /* frame_h */ 33 | -------------------------------------------------------------------------------- /mirrorpi/Makefile: -------------------------------------------------------------------------------- 1 | 2 | #SRC = main.c audio.c controls.c frame.c ringbuffer.c serial.c stream.c 3 | CC = gcc 4 | OPT = -g -Os 5 | SRC = $(wildcard *.c) 6 | OBJS = $(SRC:.c=.o) 7 | #CFLAGS = $(shell sdl2-config --cflags) -fsanitize=address 8 | CFLAGS = -Wall -Wsign-conversion $(shell sdl2-config --cflags) 9 | #LIBS = $(shell sdl2-config --libs) -latomic -fsanitize=address -static-libasan 10 | LIBS = $(shell sdl2-config --libs) 11 | 12 | all: mirror 13 | 14 | rpi: CFLAGS += -DTARGET_RPI 15 | rpi: LIBS += -ludev -lpigpio 16 | rpi: mirror 17 | 18 | macos: CFLAGS += -DTARGET_MACOS 19 | macos: LIBS += -framework Foundation -framework IOKit 20 | macos: mirror 21 | 22 | mirror: $(OBJS) 23 | $(CC) $(OBJS) $(LIBS) -o mirror 24 | 25 | debug: CFLAGS += -DDEBUG 26 | debug: rpi 27 | 28 | %.o: %.c 29 | $(CC) -c $(OPT) -I . $(CFLAGS) $< -o $@ 30 | 31 | clean: 32 | rm -f $(OBJS) mirror 33 | -------------------------------------------------------------------------------- /mirrorpi/constants.h: -------------------------------------------------------------------------------- 1 | 2 | #define VENDOR_NAME "Panic" 3 | #define APP_NAME "mirror" // file names, keys, etc. 4 | #define APP_DISPLAY "Mirror" // user display, window titles 5 | 6 | #define FRAME_WIDTH 400 7 | #define FRAME_HEIGHT 240 8 | #define ROW_SIZE_BYTES (FRAME_WIDTH/8) 9 | #define FRAME_SIZE_BYTES (ROW_SIZE_BYTES * FRAME_HEIGHT) 10 | 11 | #define NUM_BUTTONS 8 12 | 13 | #define BLACK_R 0x31 14 | #define BLACK_G 0x2f 15 | #define BLACK_B 0x28 16 | 17 | #define WHITE_R 0xb1 18 | #define WHITE_G 0xaf 19 | #define WHITE_B 0xa8 20 | 21 | #define YELLOW_R 0xfb 22 | #define YELLOW_G 0xc6 23 | #define YELLOW_B 0x51 24 | 25 | #define COLOR_TRIPLE(color) color##_R, color##_G, color##_B 26 | 27 | enum { 28 | TIMER_StreamProtocolEnable = 1, 29 | TIMER_StreamProtocolStart = 2, 30 | TIMER_StreamProtocolPoke = 3, 31 | TIMER_GameControllerHandlerPoll = 4, 32 | TIMER_InputHandlerSendUpdate = 5, 33 | }; 34 | -------------------------------------------------------------------------------- /colorknot/mini3d/mini3d.h: -------------------------------------------------------------------------------- 1 | // 2 | // mini3d.h 3 | // Extension 4 | // 5 | // Created by Dave Hayden on 10/20/15. 6 | // Copyright © 2015 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #ifndef mini3d_h 10 | #define mini3d_h 11 | 12 | // Generally, you'd only need one of these, as they're two ways of solving the same problem: 13 | 14 | // The Z buffer is slower but more accurate, and can handle intersecting geometry. 15 | #define ENABLE_Z_BUFFER 1 16 | 17 | // The ordering table is faster but can produce glitches if the table is too small or there's 18 | // intersecting geometry in the scene. 19 | #define ENABLE_ORDERING_TABLE 1 20 | 21 | #include 22 | 23 | extern void* (*m3d_realloc)(void* ptr, size_t size); 24 | 25 | #define m3d_malloc(s) m3d_realloc(NULL, (s)) 26 | #define m3d_free(ptr) m3d_realloc((ptr), 0) 27 | 28 | void mini3d_setRealloc(void* (*realloc)(void* ptr, size_t size)); 29 | 30 | #endif /* mini3d_h */ 31 | -------------------------------------------------------------------------------- /colorknot/Makefile: -------------------------------------------------------------------------------- 1 | HEAP_SIZE = 8388208 2 | STACK_SIZE = 61800 3 | 4 | PRODUCT = 3DLibrary.pdx 5 | 6 | SDK = ${PLAYDATE_SDK_PATH} 7 | ifeq ($(SDK),) 8 | SDK = $(shell egrep '^\s*SDKRoot' ~/.Playdate/config | head -n 1 | cut -c9-) 9 | endif 10 | 11 | ifeq ($(SDK),) 12 | $(error SDK path not found; set ENV value PLAYDATE_SDK_PATH) 13 | endif 14 | 15 | VPATH += mini3d 16 | 17 | # List C source files here 18 | SRC = \ 19 | main.c \ 20 | mini3d/mini3d.c \ 21 | mini3d/3dmath.c \ 22 | mini3d/scene.c \ 23 | mini3d/shape.c \ 24 | mini3d/render.c \ 25 | luaglue.c 26 | 27 | ASRC = setup.s 28 | 29 | # List all user directories here 30 | UINCDIR = mini3d 31 | 32 | # List all user C define here, like -D_DEBUG=1 33 | UDEFS = 34 | 35 | # Define ASM defines here 36 | UADEFS = 37 | 38 | # List the user directory to look for the libraries here 39 | ULIBDIR = 40 | 41 | # List all user libraries here 42 | ULIBS = 43 | 44 | #CLANGFLAGS = -fsanitize=address 45 | 46 | include $(SDK)/C_API/buildsupport/common.mk 47 | -------------------------------------------------------------------------------- /colorknot/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // main.c 3 | // Extension 4 | // 5 | // Created by Dave Hayden on 7/30/14. 6 | // Copyright (c) 2014 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | 12 | #include "pd_api.h" 13 | #include "luaglue.h" 14 | 15 | #ifdef _WINDLL 16 | __declspec(dllexport) 17 | #endif 18 | int eventHandler(PlaydateAPI* playdate, PDSystemEvent event, uint32_t arg) 19 | { 20 | if ( event == kEventInitLua ) 21 | register3D(playdate); 22 | else if ( event == kEventStreamStarted ) 23 | { 24 | uint8_t palette[3*16] = { 25 | 0x00, 0x00, 0x00, 26 | 0x11, 0x11, 0x11, 27 | 0x22, 0x22, 0x22, 28 | 0x33, 0x33, 0x33, 29 | 0x44, 0x44, 0x44, 30 | 0x55, 0x55, 0x55, 31 | 0x66, 0x66, 0x66, 32 | 0x77, 0x77, 0x77, 33 | 0x88, 0x88, 0x88, 34 | 0x99, 0x99, 0x99, 35 | 0xaa, 0xaa, 0xaa, 36 | 0xbb, 0xbb, 0xbb, 37 | 0xcc, 0xcc, 0xcc, 38 | 0xdd, 0xdd, 0xdd, 39 | 0xee, 0xee, 0xee, 40 | 0xff, 0xff, 0xff, 41 | }; 42 | 43 | playdate->system->sendMirrorData(2, palette, 3*16); 44 | } 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 daveatpanic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Playdate Cabinet 2 | CAD and code for the Playdate Cabinet 3 | 4 | mirrorpi/ - Mirror port for the Raspberry Pi 5 | crank/ - KiCad files for crank sensor PCB, Arduino sketch for Seeeduino Xiao controller 6 | cabinet.skp - SketchUp CAD file for cabinet 7 | 8 | BOM 9 | 10 | Display: Scepter ‎E205W-16003R 20" 1600x900 75Hz Ultra Thin LED Monitor 11 | Raspberry Pi 3 B+ 12 | EG STARTS JXGF-5Pin-Stick Arcade Joystick 13 | 3x Reyann Black Happ Type Standard Arcade Push Button with Microswitch 14 | 15 | Paint: Rustoleum Painter's Touch 2x Golden Sunset 16 | 17 | raspi-config: enable serial, but not login over serial 18 | 19 | add `dwc_otg.speed=1 video=HDMI-A-1:1280x720M@60` to cmdline.txt 20 | 21 | add 22 | 23 | hdmi_group=1 24 | hdmi_drive=2 25 | 26 | to [all] in config.txt, create /etc/asound.conf: 27 | 28 | defaults.pcm.card 1 29 | defaults.ctl.card 1 30 | 31 | do crontab -e, add 32 | 33 | @reboot /home/pi/runmirror.sh 34 | 35 | _Playdate Cabinet is a side project, not a Panic product! Please email dave@panic.com with questions, not Playdate support!_ 36 | 37 | ![Playdate cabinets at Fantastic Arcade 2024](IMG_8115.webp "Cabinets") 38 | -------------------------------------------------------------------------------- /colorknot/Source/ztest.lua: -------------------------------------------------------------------------------- 1 | 2 | -- test z buffer by drawing a square rotating through another square 3 | 4 | scene = lib3d.scene.new() 5 | scene:setCameraOrigin(0, 0, 4) 6 | scene:setLight(0.2, 0.8, 0.4) 7 | 8 | n = scene:getRootNode() 9 | 10 | square1 = lib3d.shape.new() 11 | 12 | square1:addFace(lib3d.point.new(-2,-2,0), 13 | lib3d.point.new(2,-2,0), 14 | lib3d.point.new(2,2,0), 15 | lib3d.point.new(-2,2,0), 0) 16 | 17 | n1 = n:addChildNode() 18 | n1:addShape(square1) 19 | 20 | square2 = lib3d.shape.new() 21 | 22 | square2:addFace(lib3d.point.new(-1,-1,0), 23 | lib3d.point.new(1,-1,0), 24 | lib3d.point.new(1,1,0), 25 | lib3d.point.new(-1,1,0), -0.5) 26 | 27 | n2 = n:addChildNode() 28 | n2:addShape(square2) 29 | 30 | local gfx = playdate.graphics 31 | 32 | rot = lib3d.matrix.newRotation(1,0,1,0) 33 | r = 0 34 | 35 | -- up/down arrows roll the scene forward and backwards 36 | function playdate.upButtonDown() r -= 5 end 37 | function playdate.downButtonDown() r += 5 end 38 | 39 | function playdate.update() 40 | 41 | n = scene:getRootNode() 42 | n:setTransform(lib3d.matrix.newRotation(r,1,0,0)) 43 | 44 | n2:addTransform(rot) 45 | 46 | gfx.clear(gfx.kColorBlack) 47 | scene:draw() 48 | end 49 | 50 | 51 | -------------------------------------------------------------------------------- /colorknot/mini3d/render.h: -------------------------------------------------------------------------------- 1 | // 2 | // render.h 3 | // Extension 4 | // 5 | // Created by Dave Hayden on 10/20/15. 6 | // Copyright © 2015 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #ifndef render_h 10 | #define render_h 11 | 12 | #include 13 | #include "3dmath.h" 14 | #include "mini3d.h" 15 | 16 | typedef struct 17 | { 18 | int16_t start; 19 | int16_t end; 20 | } LCDRowRange; 21 | 22 | LCDRowRange drawLine(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, int thick, uint8_t color); 23 | LCDRowRange fillTriangle(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, Point3D* p3, uint8_t color); 24 | LCDRowRange fillQuad(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, Point3D* p3, Point3D* p4, uint8_t color); 25 | 26 | #if ENABLE_Z_BUFFER 27 | void resetZBuffer(float zmin); 28 | 29 | LCDRowRange drawLine_zbuf(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, int thick, uint8_t color); 30 | LCDRowRange fillTriangle_zbuf(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, Point3D* p3, uint8_t color); 31 | LCDRowRange fillQuad_zbuf(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, Point3D* p3, Point3D* p4, uint8_t color); 32 | #endif 33 | 34 | #endif /* render_h */ 35 | -------------------------------------------------------------------------------- /colorknot/mini3d/shape.h: -------------------------------------------------------------------------------- 1 | // 2 | // shape.h 3 | // Extension 4 | // 5 | // Created by Dave Hayden on 10/7/15. 6 | // Copyright © 2015 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #ifndef shape_h 10 | #define shape_h 11 | 12 | #include "mini3d.h" 13 | #include "3dmath.h" 14 | 15 | typedef struct 16 | { 17 | uint16_t p1; 18 | uint16_t p2; 19 | uint16_t p3; 20 | uint16_t p4; // 0xffff if it's a tri 21 | float colorBias; 22 | } Face3D; 23 | 24 | typedef struct 25 | { 26 | int retainCount; 27 | int nPoints; 28 | Point3D* points; 29 | int nFaces; 30 | Face3D* faces; 31 | Point3D center; // used for z-sorting entire shapes at a time 32 | float colorBias; 33 | int isClosed : 1; 34 | #if ENABLE_ORDERING_TABLE 35 | int orderTableSize; 36 | #endif 37 | } Shape3D; 38 | 39 | void Shape3D_init(Shape3D* shape); 40 | void Shape3D_initWithPrototype(Shape3D* shape, Shape3D* proto); 41 | 42 | Shape3D* Shape3D_retain(Shape3D* shape); 43 | void Shape3D_release(Shape3D* shape); 44 | 45 | void Shape3D_addFace(Shape3D* shape, Point3D* a, Point3D* b, Point3D* c, Point3D* d, float colorBias); 46 | 47 | void Shape3D_setClosed(Shape3D* shape, int flag); 48 | 49 | #if ENABLE_ORDERING_TABLE 50 | void Shape3D_setOrderTableSize(Shape3D* shape, int size); 51 | #endif 52 | 53 | #endif /* shape_h */ 54 | -------------------------------------------------------------------------------- /colorknot/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | set(CMAKE_C_STANDARD 11) 3 | 4 | set(ENVSDK $ENV{PLAYDATE_SDK_PATH}) 5 | 6 | if (NOT ${ENVSDK} STREQUAL "") 7 | # Convert path from Windows 8 | file(TO_CMAKE_PATH ${ENVSDK} SDK) 9 | else() 10 | execute_process( 11 | COMMAND bash -c "egrep '^\\s*SDKRoot' $HOME/.Playdate/config" 12 | COMMAND head -n 1 13 | COMMAND cut -c9- 14 | OUTPUT_VARIABLE SDK 15 | OUTPUT_STRIP_TRAILING_WHITESPACE 16 | ) 17 | endif() 18 | 19 | if (NOT EXISTS ${SDK}) 20 | message(FATAL_ERROR "SDK Path not found; set ENV value PLAYDATE_SDK_PATH") 21 | return() 22 | endif() 23 | 24 | set(CMAKE_CONFIGURATION_TYPES "Debug;Release") 25 | set(CMAKE_XCODE_GENERATE_SCHEME TRUE) 26 | 27 | # Game Name Customization 28 | set(PLAYDATE_GAME_NAME 3DLibrary) 29 | set(PLAYDATE_GAME_DEVICE 3DLibrary_DEVICE) 30 | 31 | project(${PLAYDATE_GAME_NAME} C ASM) 32 | 33 | file(GLOB MINI3D_GLOB 34 | "mini3d/*.c" 35 | "mini3d/*.h" 36 | ) 37 | 38 | file(GLOB CORE_HEADERS 39 | "core_*.h" 40 | ) 41 | 42 | file(GLOB LUA_CODE 43 | "Source/*.lua" 44 | ) 45 | 46 | include_directories("mini3d") 47 | 48 | if (TOOLCHAIN STREQUAL "armgcc") 49 | add_executable(${PLAYDATE_GAME_DEVICE} main.c luaglue.c ${MINI3D_GLOB} ) 50 | else() 51 | add_library(${PLAYDATE_GAME_NAME} SHARED main.c luaglue.c luaglue.h ${CORE_HEADERS} ${MINI3D_GLOB} ${LUA_CODE}) 52 | endif() 53 | 54 | include(${SDK}/C_API/buildsupport/playdate_game.cmake) 55 | 56 | -------------------------------------------------------------------------------- /mirrorpi/ringbuffer.h: -------------------------------------------------------------------------------- 1 | // 2 | // ringbuffer.h 3 | // Playdate Simulator 4 | // 5 | // Created by Dave Hayden on 5/21/18. 6 | // Copyright © 2018 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #ifndef ringbuffer_h 10 | #define ringbuffer_h 11 | 12 | #include 13 | #include 14 | 15 | typedef struct 16 | { 17 | void* buffer; 18 | unsigned int bufferlen; 19 | unsigned int datasize; // number of bytes in a frame 20 | unsigned int inpos; // offset of the input/read pointer (this moves when we read data from disk) 21 | unsigned int outpos; // offset of the output pointer (this moves when we push audio to output) 22 | } RingBuffer; 23 | 24 | void RingBuffer_init(RingBuffer* r); 25 | void RingBuffer_deinit(RingBuffer* r); 26 | 27 | bool RingBuffer_setSize(RingBuffer* r, unsigned int bytes, unsigned int datasize); 28 | unsigned int RingBuffer_getSize(RingBuffer* r); 29 | 30 | // clear inpos, outpos, and fileOffset 31 | void RingBuffer_reset(RingBuffer* r); 32 | 33 | unsigned int RingBuffer_getFreeSpace(RingBuffer* r); 34 | unsigned int RingBuffer_getBytesAvailable(RingBuffer* r); 35 | 36 | // returns number of bytes actually copied, if length is > space available 37 | unsigned int RingBuffer_addData(RingBuffer* r, const void* data, unsigned int length); 38 | 39 | // returns number of bytes actually copied, if bytes > available data 40 | unsigned int RingBuffer_readData(RingBuffer* r, void* data, unsigned int bytes); 41 | 42 | // direct access to buffer data: 43 | unsigned int RingBuffer_getInputAvailableSize(RingBuffer* r); // contiguous space 44 | void* RingBuffer_getInputPointer(RingBuffer* r); 45 | void RingBuffer_moveInputPointer(RingBuffer* r, unsigned int bytes); 46 | 47 | unsigned int RingBuffer_getOutputAvailableSize(RingBuffer* r); // contiguous space 48 | void* RingBuffer_getOutputPointer(RingBuffer* r); 49 | void RingBuffer_moveOutputPointer(RingBuffer* r, unsigned int bytes); 50 | 51 | #endif /* ringbuffer_h */ 52 | -------------------------------------------------------------------------------- /mirrorpi/stream.h: -------------------------------------------------------------------------------- 1 | #ifndef stream_h 2 | #define stream_h 3 | 4 | #include 5 | #include "constants.h" 6 | #include "frame.h" 7 | 8 | enum 9 | { 10 | OPCODE_DEVICE_STATE = 1, 11 | OPCODE_FRAME_BEGIN_DEPRECATED = 10, 12 | OPCODE_FRAME_END = 11, 13 | OPCODE_FRAME_ROW = 12, 14 | OPCODE_FRAME_BEGIN = 13, 15 | OPCODE_FULL_FRAME = 14, 16 | OPCODE_AUDIO_FRAME = 20, 17 | OPCODE_AUDIO_CHANGE = 21, 18 | OPCODE_AUDIO_OFFSET = 22, 19 | OPCODE_APPLICATION = 0x99, 20 | }; 21 | 22 | enum 23 | { 24 | APPLICATION_COMMAND_RESET = 0, 25 | APPLICATION_COMMAND_1BIT_PALETTE = 1, 26 | APPLICATION_COMMAND_4BIT_PALETTE = 2 27 | }; 28 | 29 | typedef struct 30 | { 31 | uint8_t opcode; 32 | uint8_t unused; 33 | uint16_t payload_length; 34 | } MessageHeader; 35 | 36 | typedef struct 37 | { 38 | uint8_t buttonMask; 39 | uint8_t stateBits; 40 | uint16_t unused; 41 | float crankAngle; 42 | } MessageDeviceState; 43 | 44 | typedef struct 45 | { 46 | uint32_t timestamp_ms; // ms since beginning of the stream 47 | } MessageFrameBegin; 48 | 49 | typedef struct 50 | { 51 | uint8_t rowNum; 52 | uint8_t data[ROW_SIZE_BYTES]; 53 | uint8_t zero; 54 | } MessageRowData; 55 | 56 | typedef struct 57 | { 58 | uint32_t timestamp_ms; // ms since beginning of the stream 59 | uint8_t rowmask[FRAME_HEIGHT/8]; 60 | uint8_t data[]; // ROW_SIZE_BYTES * rowmask popcount 61 | } MessageFrameData; 62 | 63 | typedef struct 64 | { 65 | RGB palette[2]; 66 | } Message1bitPalette; 67 | 68 | typedef struct 69 | { 70 | RGB palette[16]; 71 | } Message4bitPalette; 72 | 73 | typedef struct 74 | { 75 | uint8_t data[0]; 76 | } MessageAudioFrame; 77 | 78 | typedef struct 79 | { 80 | uint16_t flags; 81 | } MessageAudioChange; 82 | 83 | typedef struct 84 | { 85 | uint32_t offset_samples; 86 | } MessageAudioOffset; 87 | 88 | #define STREAM_AUDIO_FLAG_ENABLED (1 << 0) 89 | #define STREAM_AUDIO_FLAG_STEREO (1 << 1) 90 | 91 | enum StreamAudioConfig 92 | { 93 | kAudioDisabled, 94 | kAudioStereo16, 95 | kAudioMono16, 96 | }; 97 | 98 | enum StreamAudioMute 99 | { 100 | kAudioMuteOn, 101 | kAudioMuteOff 102 | }; 103 | 104 | bool stream_init(); 105 | void stream_begin(); 106 | void stream_poke(); 107 | void stream_addData(uint8_t* buf, unsigned int len); 108 | bool stream_process(); 109 | void stream_reset(); 110 | 111 | void stream_sendButtonPress(int btn); 112 | void stream_sendButtonRelease(int btn); 113 | void stream_sendCrankChange(float angle_change); 114 | void stream_sendCrankDocked(bool flag); 115 | 116 | #endif // stream_h 117 | -------------------------------------------------------------------------------- /colorknot/mini3d/3dmath.h: -------------------------------------------------------------------------------- 1 | // 2 | // pd3d.h 3 | // Playdate Simulator 4 | // 5 | // Created by Dave Hayden on 8/25/15. 6 | // Copyright (c) 2015 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #ifndef __Playdate_Simulator__pd3d__ 10 | #define __Playdate_Simulator__pd3d__ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #if TARGET_PLAYDATE 17 | static inline float _lib3d_sqrtf(float x) 18 | { 19 | float result; 20 | asm ("vsqrt.f32 %0, %1" : "=t"(result) : "t"(x)); 21 | return result; 22 | } 23 | #define sqrtf(f) _lib3d_sqrtf(f) 24 | #endif 25 | 26 | typedef struct 27 | { 28 | float x; 29 | float y; 30 | float z; 31 | } Point3D; 32 | 33 | static inline Point3D Point3DMake(float x, float y, float z) 34 | { 35 | return (Point3D){ .x = x, .y = y, .z = z }; 36 | } 37 | 38 | static inline int Point3D_equals(Point3D a, Point3D b) 39 | { 40 | return (a.x == b.x) && (a.y == b.y) && (a.z == b.z); 41 | } 42 | 43 | typedef struct 44 | { 45 | float dx; 46 | float dy; 47 | float dz; 48 | } Vector3D; 49 | 50 | static inline Vector3D Vector3DMake(float dx, float dy, float dz) 51 | { 52 | return (Vector3D){ .dx = dx, .dy = dy, .dz = dz }; 53 | } 54 | 55 | static inline Vector3D Vector3DCross(Vector3D a, Vector3D b) 56 | { 57 | return (Vector3D){ .dx = a.dy * b.dz - a.dz * b.dy, .dy = a.dz * b.dx - a.dx * b.dz, .dz = a.dx * b.dy - a.dy * b.dx }; 58 | } 59 | 60 | static inline float Vector3DDot(Vector3D a, Vector3D b) 61 | { 62 | return a.dx * b.dx + a.dy * b.dy + a.dz * b.dz; 63 | } 64 | 65 | Vector3D pnormal(Point3D* p1, Point3D* p2, Point3D* p3); 66 | 67 | static inline float Vector3D_lengthSquared(Vector3D* v) 68 | { 69 | return v->dx * v->dx + v->dy * v->dy + v->dz * v->dz; 70 | } 71 | 72 | static inline float Vector3D_length(Vector3D* v) 73 | { 74 | return sqrtf(Vector3D_lengthSquared(v)); 75 | } 76 | 77 | static inline Point3D Point3D_addVector(Point3D a, Vector3D v) 78 | { 79 | return Point3DMake(a.x + v.dx, a.y + v.dy, a.z + v.dz); 80 | } 81 | 82 | // XXX - which side are we multiplying on? 83 | 84 | typedef struct 85 | { 86 | unsigned int isIdentity : 1; 87 | unsigned int inverting : 1; 88 | float m[3][3]; 89 | float dx; 90 | float dy; 91 | float dz; 92 | } Matrix3D; 93 | 94 | extern Matrix3D identityMatrix; 95 | 96 | Matrix3D Matrix3DMake(float m11, float m12, float m13, float m21, float m22, float m23, float m31, float m32, float m33, int inverting); 97 | Matrix3D Matrix3DMakeTranslate(float dx, float dy, float dz); 98 | Matrix3D Matrix3D_multiply(Matrix3D l, Matrix3D r); 99 | Point3D Matrix3D_apply(Matrix3D m, Point3D p); 100 | 101 | float Matrix3D_getDeterminant(Matrix3D* m); 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /colorknot/mini3d/shape.c: -------------------------------------------------------------------------------- 1 | // 2 | // shape.c 3 | // Extension 4 | // 5 | // Created by Dave Hayden on 10/7/15. 6 | // Copyright © 2015 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #include "mini3d.h" 10 | #include "shape.h" 11 | 12 | void Shape3D_init(Shape3D* shape) 13 | { 14 | shape->retainCount = 0; 15 | shape->nPoints = 0; 16 | shape->points = NULL; 17 | shape->nFaces = 0; 18 | shape->faces = NULL; 19 | shape->center.x = 0; 20 | shape->center.y = 0; 21 | shape->center.z = 0; 22 | shape->colorBias = 0; 23 | shape->isClosed = 0; 24 | #if ENABLE_ORDERING_TABLE 25 | shape->orderTableSize = 0; 26 | #endif 27 | } 28 | 29 | Shape3D* Shape3D_retain(Shape3D* shape) 30 | { 31 | ++shape->retainCount; 32 | return shape; 33 | } 34 | 35 | void Shape3D_release(Shape3D* shape) 36 | { 37 | if ( --shape->retainCount > 0 ) 38 | return; 39 | 40 | if ( shape->points != NULL ) 41 | m3d_free(shape->points); 42 | 43 | if ( shape->faces != NULL ) 44 | m3d_free(shape->faces); 45 | 46 | m3d_free(shape); 47 | } 48 | 49 | int Shape3D_addPoint(Shape3D* shape, Point3D* p) 50 | { 51 | int i; 52 | 53 | for ( i = 0; i < shape->nPoints; ++i ) 54 | { 55 | if ( Point3D_equals(*p, shape->points[i]) ) 56 | return i; 57 | } 58 | 59 | shape->points = m3d_realloc(shape->points, (shape->nPoints + 1) * sizeof(Point3D)); 60 | 61 | Point3D* point = &shape->points[shape->nPoints]; 62 | *point = *p; 63 | 64 | return shape->nPoints++; 65 | } 66 | 67 | void Shape3D_addFace(Shape3D* shape, Point3D* a, Point3D* b, Point3D* c, Point3D* d, float colorBias) 68 | { 69 | shape->faces = m3d_realloc(shape->faces, (shape->nFaces + 1) * sizeof(Face3D)); 70 | 71 | Face3D* face = &shape->faces[shape->nFaces]; 72 | face->p1 = Shape3D_addPoint(shape, a); 73 | face->p2 = Shape3D_addPoint(shape, b); 74 | face->p3 = Shape3D_addPoint(shape, c); 75 | face->p4 = (d != NULL) ? Shape3D_addPoint(shape, d) : 0xffff; 76 | face->colorBias = colorBias; 77 | 78 | ++shape->nFaces; 79 | 80 | if ( d != NULL ) 81 | { 82 | shape->center.x += (a->x + b->x + c->x + d->x) / 4 / shape->nFaces; 83 | shape->center.y += (a->y + b->y + c->y + d->y) / 4 / shape->nFaces; 84 | shape->center.z += (a->z + b->z + c->z + d->z) / 4 / shape->nFaces; 85 | } 86 | else 87 | { 88 | shape->center.x += (a->x + b->x + c->x) / 3 / shape->nFaces; 89 | shape->center.y += (a->y + b->y + c->y) / 3 / shape->nFaces; 90 | shape->center.z += (a->z + b->z + c->z) / 3 / shape->nFaces; 91 | } 92 | } 93 | 94 | void Shape3D_setClosed(Shape3D* shape, int flag) 95 | { 96 | shape->isClosed = flag; 97 | } 98 | 99 | #if ENABLE_ORDERING_TABLE 100 | void Shape3D_setOrderTableSize(Shape3D* shape, int size) 101 | { 102 | shape->orderTableSize = size; 103 | } 104 | #endif 105 | -------------------------------------------------------------------------------- /crank/sensor1/sensor.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 7, 4 | "active_layer_preset": "", 5 | "auto_track_width": false, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 1, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "shapes": 1.0, 14 | "tracks": 1.0, 15 | "vias": 1.0, 16 | "zones": 0.6 17 | }, 18 | "selection_filter": { 19 | "dimensions": true, 20 | "footprints": true, 21 | "graphics": true, 22 | "keepouts": true, 23 | "lockedItems": false, 24 | "otherItems": true, 25 | "pads": true, 26 | "text": true, 27 | "tracks": true, 28 | "vias": true, 29 | "zones": true 30 | }, 31 | "visible_items": [ 32 | "vias", 33 | "footprint_text", 34 | "footprint_anchors", 35 | "ratsnest", 36 | "grid", 37 | "footprints_front", 38 | "footprints_back", 39 | "footprint_values", 40 | "footprint_references", 41 | "tracks", 42 | "drc_errors", 43 | "drawing_sheet", 44 | "bitmaps", 45 | "pads", 46 | "zones", 47 | "drc_warnings", 48 | "locked_item_shadows", 49 | "conflict_shadows", 50 | "shapes" 51 | ], 52 | "visible_layers": "00000000_00000000_55555555_575555f5", 53 | "zone_display_mode": 0 54 | }, 55 | "git": { 56 | "repo_password": "", 57 | "repo_type": "", 58 | "repo_username": "", 59 | "ssh_key": "" 60 | }, 61 | "meta": { 62 | "filename": "sensor.kicad_prl", 63 | "version": 5 64 | }, 65 | "net_inspector_panel": { 66 | "col_hidden": [ 67 | false, 68 | false, 69 | false, 70 | false, 71 | false, 72 | false, 73 | false, 74 | false, 75 | false, 76 | false 77 | ], 78 | "col_order": [ 79 | 0, 80 | 1, 81 | 2, 82 | 3, 83 | 4, 84 | 5, 85 | 6, 86 | 7, 87 | 8, 88 | 9 89 | ], 90 | "col_widths": [ 91 | 156, 92 | 141, 93 | 103, 94 | 71, 95 | 103, 96 | 103, 97 | 103, 98 | 74, 99 | 103, 100 | 103 101 | ], 102 | "custom_group_rules": [], 103 | "expanded_rows": [], 104 | "filter_by_net_name": true, 105 | "filter_by_netclass": true, 106 | "filter_text": "", 107 | "group_by_constraint": false, 108 | "group_by_netclass": false, 109 | "show_unconnected_nets": false, 110 | "show_zero_pad_nets": false, 111 | "sort_ascending": true, 112 | "sorting_column": 0 113 | }, 114 | "open_jobsets": [], 115 | "project": { 116 | "files": [] 117 | }, 118 | "schematic": { 119 | "selection_filter": { 120 | "graphics": true, 121 | "images": true, 122 | "labels": true, 123 | "lockedItems": false, 124 | "otherItems": true, 125 | "pins": true, 126 | "symbols": true, 127 | "text": true, 128 | "wires": true 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /crank/sensor2/sensor2.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 7, 4 | "active_layer_preset": "All Layers", 5 | "auto_track_width": false, 6 | "hidden_netclasses": [], 7 | "hidden_nets": [], 8 | "high_contrast_mode": 0, 9 | "net_color_mode": 1, 10 | "opacity": { 11 | "images": 0.6, 12 | "pads": 1.0, 13 | "shapes": 1.0, 14 | "tracks": 1.0, 15 | "vias": 1.0, 16 | "zones": 0.6 17 | }, 18 | "selection_filter": { 19 | "dimensions": true, 20 | "footprints": true, 21 | "graphics": true, 22 | "keepouts": true, 23 | "lockedItems": false, 24 | "otherItems": true, 25 | "pads": true, 26 | "text": true, 27 | "tracks": true, 28 | "vias": true, 29 | "zones": true 30 | }, 31 | "visible_items": [ 32 | "vias", 33 | "footprint_text", 34 | "footprint_anchors", 35 | "ratsnest", 36 | "grid", 37 | "footprints_front", 38 | "footprints_back", 39 | "footprint_values", 40 | "footprint_references", 41 | "tracks", 42 | "drc_errors", 43 | "drawing_sheet", 44 | "bitmaps", 45 | "pads", 46 | "zones", 47 | "drc_warnings", 48 | "locked_item_shadows", 49 | "conflict_shadows", 50 | "shapes" 51 | ], 52 | "visible_layers": "ffffffff_ffffffff_ffffffff_ffffffff", 53 | "zone_display_mode": 0 54 | }, 55 | "git": { 56 | "repo_password": "", 57 | "repo_type": "", 58 | "repo_username": "", 59 | "ssh_key": "" 60 | }, 61 | "meta": { 62 | "filename": "sensor2.kicad_prl", 63 | "version": 5 64 | }, 65 | "net_inspector_panel": { 66 | "col_hidden": [ 67 | false, 68 | false, 69 | false, 70 | false, 71 | false, 72 | false, 73 | false, 74 | false, 75 | false, 76 | false 77 | ], 78 | "col_order": [ 79 | 0, 80 | 1, 81 | 2, 82 | 3, 83 | 4, 84 | 5, 85 | 6, 86 | 7, 87 | 8, 88 | 9 89 | ], 90 | "col_widths": [ 91 | 156, 92 | 141, 93 | 103, 94 | 71, 95 | 103, 96 | 103, 97 | 103, 98 | 74, 99 | 103, 100 | 103 101 | ], 102 | "custom_group_rules": [], 103 | "expanded_rows": [], 104 | "filter_by_net_name": true, 105 | "filter_by_netclass": true, 106 | "filter_text": "", 107 | "group_by_constraint": false, 108 | "group_by_netclass": false, 109 | "show_unconnected_nets": false, 110 | "show_zero_pad_nets": false, 111 | "sort_ascending": true, 112 | "sorting_column": 0 113 | }, 114 | "open_jobsets": [], 115 | "project": { 116 | "files": [] 117 | }, 118 | "schematic": { 119 | "selection_filter": { 120 | "graphics": true, 121 | "images": true, 122 | "labels": true, 123 | "lockedItems": false, 124 | "otherItems": true, 125 | "pins": true, 126 | "symbols": true, 127 | "text": true, 128 | "wires": true 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /mirrorpi/controls.c: -------------------------------------------------------------------------------- 1 | // 2 | // controls.c 3 | // MirrorJr 4 | // 5 | // Created by Dave Hayden on 9/28/24. 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "controls.h" 13 | #include "stream.h" 14 | 15 | #if TARGET_RPI 16 | #include 17 | #endif 18 | 19 | // buttons are on the following GPIOs, crank is handled by an external 20 | // microcontroller which sends us movement data over /dev/ttyS0 21 | 22 | enum buttons 23 | { 24 | kButtonUp, 25 | kButtonDown, 26 | kButtonLeft, 27 | kButtonRight, 28 | kButtonB, 29 | kButtonA, 30 | kButtonMenu 31 | }; 32 | 33 | int pi = 0; 34 | const unsigned int gpios[] = { 4, 27, 22, 23, 24, 25, 5 }; 35 | unsigned int crank = 0; 36 | 37 | bool controls_init() 38 | { 39 | int res = gpioInitialise(); 40 | 41 | if ( res < 0 ) 42 | { 43 | printf("gpioInitialise failed\n"); 44 | return false; 45 | } 46 | 47 | int dev; 48 | 49 | if ( (dev = serOpen("/dev/ttyS0", 115200, 0)) < 0 ) 50 | { 51 | printf("crank init failed\n"); 52 | return false; 53 | } 54 | 55 | crank = (unsigned)dev; 56 | 57 | if ( gpioSetMode(4, PI_INPUT) < 0 || gpioSetPullUpDown(4, PI_PUD_UP) || 58 | gpioSetMode(27, PI_INPUT) < 0 || gpioSetPullUpDown(27, PI_PUD_UP) || 59 | gpioSetMode(22, PI_INPUT) < 0 || gpioSetPullUpDown(22, PI_PUD_UP) || 60 | gpioSetMode(23, PI_INPUT) < 0 || gpioSetPullUpDown(23, PI_PUD_UP) || 61 | gpioSetMode(24, PI_INPUT) < 0 || gpioSetPullUpDown(24, PI_PUD_UP) || 62 | gpioSetMode(25, PI_INPUT) < 0 || gpioSetPullUpDown(25, PI_PUD_UP) || 63 | gpioSetMode(5, PI_INPUT) < 0 || gpioSetPullUpDown(6, PI_PUD_UP) ) 64 | { 65 | printf("gpioSetMode/PullUpDown failed\n"); 66 | return false; 67 | } 68 | 69 | return true; 70 | } 71 | 72 | int buttonon[7] = {0}; 73 | 74 | void controls_scan() 75 | { 76 | // XXX - debounce if needed 77 | 78 | for ( int i = 0; i < 7; ++i ) 79 | { 80 | int on = 1-gpioRead(gpios[i]); 81 | 82 | if ( on && !buttonon[i] ) 83 | stream_sendButtonPress(i); 84 | else if ( !on && buttonon[i] ) 85 | stream_sendButtonRelease(i); 86 | 87 | buttonon[i] = on; 88 | } 89 | 90 | static char readbuf[6] = {0}; 91 | static int readpos = 0; 92 | static float lastangle = -1; 93 | float crankangle = -1; 94 | 95 | // rate limit the crank 96 | static int limit = 0; 97 | 98 | if ( ++limit < 3 ) 99 | return; 100 | 101 | limit = 0; 102 | 103 | while ( serDataAvailable(crank) ) 104 | { 105 | int b = serReadByte(crank); 106 | 107 | if ( b == '\n' ) 108 | { 109 | if ( isdigit(readbuf[0]) ) 110 | crankangle = atof(readbuf); 111 | else if ( readpos == 4 && strncmp(readbuf, "out", 3) == 0 ) 112 | stream_sendCrankDocked(true); 113 | else if ( readpos == 3 && strncmp(readbuf, "in", 2) == 0 ) 114 | stream_sendCrankDocked(false); 115 | 116 | //printf("read %f", crankangle); 117 | readpos = 0; 118 | } 119 | else if ( readpos < 5 ) 120 | readbuf[readpos++] = b; 121 | } 122 | 123 | if ( crankangle != -1 ) 124 | { 125 | if ( lastangle != -1 ) 126 | { 127 | float change = crankangle - lastangle; 128 | 129 | if ( change > 180 ) change -= 360; 130 | else if ( change < -180 ) change += 360; 131 | 132 | if ( change != 0 ) 133 | stream_sendCrankChange(change); 134 | 135 | // printf("sending crank change %f\n", change); 136 | } 137 | 138 | lastangle = crankangle; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /colorknot/Source/knot-ordered.lua: -------------------------------------------------------------------------------- 1 | 2 | local sin = math.sin 3 | local cos = math.cos 4 | 5 | local function knot(t) 6 | t *= 2*math.pi 7 | local x = (sin(t) + 2*sin(2*t))/3 8 | local y = (cos(t) - 2*cos(2*t))/3 9 | local z = sin(3*t)/2 10 | return x,y,z 11 | end 12 | 13 | local function torus(t) 14 | t *= 2*math.pi 15 | local x = sin(t) 16 | local y = cos(t) 17 | return x,y,0 18 | end 19 | 20 | local function makeShape(func, steps, spokes, thickness, colorfunc, quad) 21 | 22 | local shape = lib3d.shape.new() 23 | local d = 0.01 24 | 25 | local function makeRing(step) 26 | local t = step/steps 27 | local x,y,z = func(t) 28 | 29 | -- compute vector at point 30 | local xm,ym,zm = func(t-d) 31 | local xp,yp,zp = func(t+d) 32 | local vx,vy,vz = xp-xm, yp-ym, zp-zm 33 | 34 | -- normalize 35 | local s = math.sqrt(vx*vx+vy*vy+vz*vz) 36 | vx /= s 37 | vy /= s 38 | vz /= s 39 | 40 | local r = math.sqrt(vx*vx+vy*vy) 41 | local ring = {} 42 | 43 | -- build ring of points around the curve point, perpendicular to v 44 | for i=0,spokes-1 do 45 | local p = i 46 | if not quad then p += (step%2)/2 end 47 | local st = 2*math.pi*p/spokes 48 | local rx = x - thickness * (sin(st)*vy + cos(st)*vx*vz) / r 49 | local ry = y + thickness * (sin(st)*vx - cos(st)*vy*vz) / r 50 | local rz = z + thickness * cos(st)*r 51 | ring[i+1] = lib3d.point.new(rx, ry, rz) 52 | end 53 | 54 | return ring 55 | end 56 | 57 | local function addRing(points1, points2, s) 58 | local n = #points1 59 | for i=1,n do 60 | local j = (i%n)+1 61 | if quad then 62 | local c = 0 63 | if colorfunc ~= nil then c = colorfunc(s,i) end 64 | shape:addFace(points1[i],points2[i],points2[j],points1[j],c) 65 | else 66 | local c1 = 0 67 | local c2 = 0 68 | if colorfunc ~= nil then 69 | c1 = colorfunc(s,i,0) 70 | c2 = colorfunc(s,i,1) 71 | end 72 | if s%2 == 0 then 73 | shape:addFace(points1[i],points2[i],points1[j],c1) 74 | shape:addFace(points2[i],points2[j],points1[j],c2) 75 | else 76 | shape:addFace(points1[i],points2[i],points2[j],c2) 77 | shape:addFace(points1[i],points2[j],points1[j],c1) 78 | end 79 | end 80 | end 81 | end 82 | 83 | local ring0 = makeRing(0) 84 | local ring = ring0 85 | 86 | for i=0,steps-2 do 87 | local next = makeRing(i+1) 88 | addRing(ring,next,i) 89 | ring = next 90 | end 91 | 92 | addRing(ring,ring0,steps-1) 93 | 94 | shape:setClosed(true) 95 | 96 | return shape 97 | end 98 | 99 | local function makeTriShape(f,st,sp,t,c) return makeShape(f,st,sp,t,c,false) end 100 | local function makeQuadShape(f,st,sp,t,c) return makeShape(f,st,sp,t,c,true) end 101 | 102 | 103 | ------ 104 | 105 | scene = lib3d.scene.new() 106 | scene:setCameraOrigin(0, 0, -4) 107 | scene:setLight(0.2, 0.8, 0.4) 108 | 109 | scene:getRootNode():scaleBy(-1,1,1) 110 | 111 | local n1 = scene:getRootNode():addChildNode() 112 | local s1 = makeTriShape(knot, 48, 16, 0.3, function(i,j,s) return 0.5-(i%2) end) 113 | s1:setOrderTableSize(50) 114 | n1:addShape(s1) 115 | 116 | local gfx = playdate.graphics 117 | 118 | playdate.display.setRefreshRate(0) 119 | rot = lib3d.matrix.newRotation(2,0,1,1) 120 | 121 | h = 0 122 | 123 | function playdate.update() 124 | 125 | setPalette(playdate.getCrankPosition()/360) 126 | 127 | gfx.clear(gfx.kColorBlack) 128 | playdate.drawFPS(0,0) 129 | n1:addTransform(rot) 130 | scene:draw() 131 | end 132 | -------------------------------------------------------------------------------- /mirrorpi/audio.c: -------------------------------------------------------------------------------- 1 | // 2 | // audio.c 3 | // MirrorJr 4 | // 5 | // Created by Dave Hayden on 9/25/24. 6 | // 7 | 8 | #include "SDL.h" 9 | #include "assert.h" 10 | 11 | #include "audio.h" 12 | #include "ringbuffer.h" 13 | 14 | #define LOG printf 15 | //#define LOG(s) 16 | 17 | #define AUDIO_SAMPLE_RATE 44100 18 | #define BUFFER_SIZE 32768 19 | unsigned int silentcount = BUFFER_SIZE; 20 | 21 | static RingBuffer buffer; 22 | 23 | SDL_AudioDeviceID soundDevice; 24 | const int SDL_FRAME_SIZE = sizeof(int16_t) * 2; 25 | unsigned int num_channels = 2; 26 | 27 | void SDLAudioCallback(void* userdata, Uint8* stream, int len) 28 | { 29 | if ( silentcount >= BUFFER_SIZE / (sizeof(int16_t) * num_channels) ) 30 | { 31 | memset(stream, 0, (unsigned)len); 32 | return; 33 | } 34 | 35 | if ( num_channels == 1 ) 36 | { 37 | int16_t* stream16 = (int16_t*)stream; 38 | int16_t s; 39 | 40 | for ( int i = 0; i < len / SDL_FRAME_SIZE; ++i ) 41 | { 42 | if ( RingBuffer_readData(&buffer, &s, 2) != 2 ) 43 | break; 44 | 45 | stream16[2*i] = stream16[2*i+1] = s; 46 | } 47 | } 48 | else 49 | RingBuffer_readData(&buffer, stream, (unsigned)len); 50 | } 51 | 52 | bool audio_init() 53 | { 54 | RingBuffer_init(&buffer); 55 | RingBuffer_setSize(&buffer, BUFFER_SIZE, SDL_FRAME_SIZE); 56 | 57 | if ( SDL_InitSubSystem(SDL_INIT_AUDIO) != 0 ) 58 | { 59 | printf("audio init failed: %s", SDL_GetError()); 60 | return false; 61 | } 62 | 63 | int numdevs = SDL_GetNumAudioDevices(0); 64 | 65 | for ( int i = 0; i < numdevs; ++i ) 66 | { 67 | SDL_AudioSpec spec; 68 | SDL_GetAudioDeviceSpec(i, 0, &spec); 69 | printf("audio dev %i: %s\n", i, SDL_GetAudioDeviceName(i, 0)); 70 | } 71 | 72 | SDL_AudioSpec want, have; 73 | SDL_memset(&want, 0, sizeof(want)); 74 | 75 | want.freq = AUDIO_SAMPLE_RATE; 76 | want.format = AUDIO_S16LSB; 77 | want.channels = 2; 78 | want.samples = 256 * SDL_FRAME_SIZE; 79 | want.callback = SDLAudioCallback; 80 | 81 | soundDevice = SDL_OpenAudioDevice 82 | ( 83 | NULL, 84 | 0, // playback 85 | &want, 86 | &have, 87 | 0); // do not SDL_AUDIO_ALLOW_FORMAT_CHANGE 88 | 89 | LOG("soundDevice: %d\n", soundDevice); 90 | 91 | return true; 92 | } 93 | 94 | void audio_setFormat(unsigned int channels) 95 | { 96 | num_channels = channels; 97 | } 98 | 99 | static bool running = false; 100 | 101 | void audio_addData(uint8_t* data, unsigned int len) 102 | { 103 | unsigned int avail = RingBuffer_getFreeSpace(&buffer); 104 | 105 | if ( avail < len ) 106 | { 107 | printf("audio buffer overflowed\n"); 108 | len = avail; 109 | } 110 | 111 | RingBuffer_addData(&buffer, data, len); 112 | silentcount = 0; 113 | 114 | if ( !running ) 115 | { 116 | SDL_PauseAudioDevice(soundDevice, 0); 117 | running = true; 118 | } 119 | } 120 | 121 | void audio_stop() 122 | { 123 | SDL_PauseAudioDevice(soundDevice, 1); 124 | RingBuffer_reset(&buffer); 125 | running = false; 126 | } 127 | 128 | #define MIN(a,b) (((a)<(b))?(a):(b)) 129 | 130 | void audio_addSilence(unsigned int len) 131 | { 132 | if ( silentcount < BUFFER_SIZE / num_channels ) 133 | { 134 | silentcount += len; 135 | 136 | while ( len > 0 ) 137 | { 138 | static const int16_t zeros[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 139 | unsigned int l = MIN(len, sizeof(zeros)); 140 | RingBuffer_addData(&buffer, zeros, l); 141 | len -= l; 142 | } 143 | } 144 | else // buffer is all zero, can just push ringbuffer pointer forward 145 | RingBuffer_moveInputPointer(&buffer, len); 146 | } 147 | -------------------------------------------------------------------------------- /colorknot/mini3d/3dmath.c: -------------------------------------------------------------------------------- 1 | // 2 | // pd3d.c 3 | // Playdate Simulator 4 | // 5 | // Created by Dave Hayden on 8/25/15. 6 | // Copyright (c) 2015 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #include 10 | #include "3dmath.h" 11 | 12 | Matrix3D identityMatrix = { .isIdentity = 1, .inverting = 0, .m = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}, .dx = 0, .dy = 0, .dz = 0 }; 13 | 14 | Matrix3D Matrix3DMake(float m11, float m12, float m13, float m21, float m22, float m23, float m31, float m32, float m33, int inverting) 15 | { 16 | return (Matrix3D){ .isIdentity = 0, .inverting = inverting, .m = {{m11, m12, m13}, {m21, m22, m23}, {m31, m32, m33}}, .dx = 0, .dy = 0, .dz = 0 }; 17 | } 18 | 19 | Matrix3D Matrix3DMakeTranslate(float dx, float dy, float dz) 20 | { 21 | return (Matrix3D){ .isIdentity = 1, .inverting = 0, .m = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}, .dx = dx, .dy = dy, .dz = dz }; 22 | } 23 | 24 | Matrix3D Matrix3D_multiply(Matrix3D l, Matrix3D r) 25 | { 26 | Matrix3D m = { .isIdentity = 0, .inverting = l.inverting ^ r.inverting }; 27 | 28 | if ( l.isIdentity ) 29 | { 30 | if ( r.isIdentity ) 31 | m = identityMatrix; 32 | else 33 | memcpy(&m.m, &r.m, sizeof(r.m)); 34 | 35 | m.dx = l.dx + r.dx; 36 | m.dy = l.dy + r.dy; 37 | m.dz = l.dz + r.dz; 38 | } 39 | else 40 | { 41 | if ( !r.isIdentity ) 42 | { 43 | m.m[0][0] = l.m[0][0] * r.m[0][0] + l.m[1][0] * r.m[0][1] + l.m[2][0] * r.m[0][2]; 44 | m.m[1][0] = l.m[0][0] * r.m[1][0] + l.m[1][0] * r.m[1][1] + l.m[2][0] * r.m[1][2]; 45 | m.m[2][0] = l.m[0][0] * r.m[2][0] + l.m[1][0] * r.m[2][1] + l.m[2][0] * r.m[2][2]; 46 | 47 | m.m[0][1] = l.m[0][1] * r.m[0][0] + l.m[1][1] * r.m[0][1] + l.m[2][1] * r.m[0][2]; 48 | m.m[1][1] = l.m[0][1] * r.m[1][0] + l.m[1][1] * r.m[1][1] + l.m[2][1] * r.m[1][2]; 49 | m.m[2][1] = l.m[0][1] * r.m[2][0] + l.m[1][1] * r.m[2][1] + l.m[2][1] * r.m[2][2]; 50 | 51 | m.m[0][2] = l.m[0][2] * r.m[0][0] + l.m[1][2] * r.m[0][1] + l.m[2][2] * r.m[0][2]; 52 | m.m[1][2] = l.m[0][2] * r.m[1][0] + l.m[1][2] * r.m[1][1] + l.m[2][2] * r.m[1][2]; 53 | m.m[2][2] = l.m[0][2] * r.m[2][0] + l.m[1][2] * r.m[2][1] + l.m[2][2] * r.m[2][2]; 54 | } 55 | else 56 | memcpy(&m.m, &l.m, sizeof(l.m)); 57 | 58 | m.dx = l.dx * r.m[0][0] + l.dy * r.m[0][1] + l.dz * r.m[0][2] + r.dx; 59 | m.dy = l.dx * r.m[1][0] + l.dy * r.m[1][1] + l.dz * r.m[1][2] + r.dy; 60 | m.dz = l.dx * r.m[2][0] + l.dy * r.m[2][1] + l.dz * r.m[2][2] + r.dz; 61 | } 62 | 63 | return m; 64 | } 65 | 66 | Point3D Matrix3D_apply(Matrix3D m, Point3D p) 67 | { 68 | if ( m.isIdentity ) 69 | return Point3DMake(p.x + m.dx, p.y + m.dy, p.z + m.dz); 70 | 71 | float x = p.x * m.m[0][0] + p.y * m.m[0][1] + p.z * m.m[0][2] + m.dx; 72 | float y = p.x * m.m[1][0] + p.y * m.m[1][1] + p.z * m.m[1][2] + m.dy; 73 | float z = p.x * m.m[2][0] + p.y * m.m[2][1] + p.z * m.m[2][2] + m.dz; 74 | 75 | return Point3DMake(x, y, z); 76 | } 77 | 78 | Vector3D Vector3D_normalize(Vector3D v) 79 | { 80 | float d = sqrtf(v.dx * v.dx + v.dy * v.dy + v.dz * v.dz); 81 | 82 | return Vector3DMake(v.dx/d, v.dy/d, v.dz/d); 83 | } 84 | 85 | Vector3D pnormal(Point3D* p1, Point3D* p2, Point3D* p3) 86 | { 87 | Vector3D v = Vector3DCross(Vector3DMake(p2->x - p1->x, p2->y - p1->y, p2->z - p1->z), 88 | Vector3DMake(p3->x - p1->x, p3->y - p1->y, p3->z - p1->z)); 89 | 90 | return Vector3D_normalize(v); 91 | } 92 | 93 | float Matrix3D_getDeterminant(Matrix3D* m) 94 | { 95 | return m->m[0][0] * m->m[1][1] * m->m[2][2] 96 | + m->m[0][1] * m->m[1][2] * m->m[2][0] 97 | + m->m[0][2] * m->m[1][0] * m->m[2][1] 98 | - m->m[2][0] * m->m[1][1] * m->m[0][2] 99 | - m->m[1][0] * m->m[0][1] * m->m[2][2] 100 | - m->m[0][0] * m->m[2][1] * m->m[1][2]; 101 | } 102 | -------------------------------------------------------------------------------- /colorknot/Source/knot.lua: -------------------------------------------------------------------------------- 1 | 2 | -- makeTriShape(knot, 64, 12, 0.3) 3 | -- z buffer: 22 FPS 4 | -- ordering table: 26 FPS 5 | 6 | 7 | local sin = math.sin 8 | local cos = math.cos 9 | 10 | local function knot(t) 11 | t *= 2*math.pi 12 | local x = (sin(t) + 2*sin(2*t))/3 13 | local y = (cos(t) - 2*cos(2*t))/3 14 | local z = sin(3*t)/2 15 | return x,y,z 16 | end 17 | 18 | local function torus(t) 19 | t *= 2*math.pi 20 | local x = sin(t) 21 | local y = cos(t) 22 | return x,y,0 23 | end 24 | 25 | local function makeShape(func, steps, spokes, thickness, colorfunc, quad) 26 | 27 | local shape = lib3d.shape.new() 28 | local d = 0.01 29 | 30 | local function makeRing(step) 31 | local t = step/steps 32 | local x,y,z = func(t) 33 | 34 | -- compute vector at point 35 | local xm,ym,zm = func(t-d) 36 | local xp,yp,zp = func(t+d) 37 | local vx,vy,vz = xp-xm, yp-ym, zp-zm 38 | 39 | -- normalize 40 | local s = math.sqrt(vx*vx+vy*vy+vz*vz) 41 | vx /= s 42 | vy /= s 43 | vz /= s 44 | 45 | local r = math.sqrt(vx*vx+vy*vy) 46 | local ring = {} 47 | 48 | -- build ring of points around the curve point, perpendicular to v 49 | for i=0,spokes-1 do 50 | local p = i 51 | if not quad then p += (step%2)/2 end 52 | local st = 2*math.pi*p/spokes 53 | local rx = x - thickness * (sin(st)*vy + cos(st)*vx*vz) / r 54 | local ry = y + thickness * (sin(st)*vx - cos(st)*vy*vz) / r 55 | local rz = z + thickness * cos(st)*r 56 | ring[i+1] = lib3d.point.new(rx, ry, rz) 57 | end 58 | 59 | return ring 60 | end 61 | 62 | local function addRing(points1, points2, s) 63 | local n = #points1 64 | for i=1,n do 65 | local j = (i%n)+1 66 | if quad then 67 | local c = 0 68 | if colorfunc ~= nil then c = colorfunc(s,i) end 69 | shape:addFace(points1[i],points2[i],points2[j],points1[j],c) 70 | else 71 | local c1 = 0 72 | local c2 = 0 73 | if colorfunc ~= nil then 74 | c1 = colorfunc(2*s,i) 75 | c2 = colorfunc(2*s+1,i) 76 | end 77 | if s%2 == 0 then 78 | shape:addFace(points1[i],points2[i],points1[j],c1) 79 | shape:addFace(points2[i],points2[j],points1[j],c2) 80 | else 81 | shape:addFace(points1[i],points2[i],points2[j],c2) 82 | shape:addFace(points1[i],points2[j],points1[j],c1) 83 | end 84 | end 85 | end 86 | end 87 | 88 | local ring0 = makeRing(0) 89 | local ring = ring0 90 | 91 | for i=0,steps-2 do 92 | local next = makeRing(i+1) 93 | addRing(ring,next,i) 94 | ring = next 95 | end 96 | 97 | addRing(ring,ring0,steps-1) 98 | 99 | shape:setClosed(true) 100 | 101 | return shape 102 | end 103 | 104 | local function makeTriShape(f,st,sp,t,c) return makeShape(f,st,sp,t,c,false) end 105 | local function makeQuadShape(f,st,sp,t,c) return makeShape(f,st,sp,t,c,true) end 106 | 107 | 108 | ------ 109 | 110 | scene = lib3d.scene.new() 111 | scene:setCameraOrigin(0, 0, -4) 112 | scene:setLight(0.2, 0.8, 0.4) 113 | 114 | local n1 = scene:getRootNode():addChildNode() 115 | local s1 = makeTriShape(knot, 48, 12, 0.45) --, function(i,j) return 0.2-(i%2) end) 116 | n1:addShape(s1) 117 | n1:setWireframeMode(1) 118 | n1:setWireframeColor(1) 119 | n1:setColorBias(-1) 120 | 121 | local n2 = scene:getRootNode():addChildNode() 122 | local s2 = makeQuadShape(torus, 16, 16, 0.5, function(i,j) return (i+j)%2 - 0.5 end) 123 | n2:addShape(s2) 124 | 125 | local gfx = playdate.graphics 126 | 127 | rot1 = lib3d.matrix.newRotation(2,0,1,1) 128 | rot2 = lib3d.matrix.newRotation(1,1,1,0) 129 | 130 | playdate.display.setRefreshRate(0) 131 | 132 | function playdate.update() 133 | gfx.clear(gfx.kColorBlack) 134 | playdate.drawFPS(0,0) 135 | 136 | n1:addTransform(rot1) 137 | n2:addTransform(rot2) 138 | 139 | scene:draw() 140 | end 141 | -------------------------------------------------------------------------------- /colorknot/Source/icosahedra.lua: -------------------------------------------------------------------------------- 1 | 2 | -- first, we compute the vertices of an icosohedron: 3 | 4 | local p = (math.sqrt(5) - 1) / 2 5 | 6 | local x1 = lib3d.point.new(0, -p, 1) 7 | local x2 = lib3d.point.new(0, p, 1) 8 | local x3 = lib3d.point.new(0, p, -1) 9 | local x4 = lib3d.point.new(0, -p, -1) 10 | 11 | local y1 = lib3d.point.new(1, 0, p) 12 | local y2 = lib3d.point.new(1, 0, -p) 13 | local y3 = lib3d.point.new(-1, 0, -p) 14 | local y4 = lib3d.point.new(-1, 0, p) 15 | 16 | local z1 = lib3d.point.new(-p, 1, 0) 17 | local z2 = lib3d.point.new(p, 1, 0) 18 | local z3 = lib3d.point.new(p, -1, 0) 19 | local z4 = lib3d.point.new(-p, -1, 0) 20 | 21 | -- now we add the faces to our prototype shape, with vertices in the 22 | -- correct order so the faces point outward (using right hand rule) 23 | 24 | shape = lib3d.shape.new() 25 | 26 | shape:addFace(z1, y3, y4) 27 | shape:addFace(z1, x3, y3) 28 | shape:addFace(z1, z2, x3) 29 | shape:addFace(z1, x2, z2) 30 | shape:addFace(z1, y4, x2) 31 | 32 | shape:addFace(y4, y3, z4) 33 | shape:addFace(z4, y3, x4) 34 | shape:addFace(y3, x3, x4) 35 | shape:addFace(x4, x3, y2) 36 | shape:addFace(x3, z2, y2) 37 | shape:addFace(y2, z2, y1) 38 | shape:addFace(z2, x2, y1) 39 | shape:addFace(y1, x2, x1) 40 | shape:addFace(x2, y4, x1) 41 | shape:addFace(x1, y4, z4) 42 | 43 | shape:addFace(z3, y2, y1) 44 | shape:addFace(z3, y1, x1) 45 | shape:addFace(z3, x1, z4) 46 | shape:addFace(z3, z4, x4) 47 | shape:addFace(z3, x4, y2) 48 | 49 | shape:setClosed(true) 50 | 51 | -- the scene object does the drawing 52 | 53 | scene = lib3d.scene.new() 54 | scene:setCameraOrigin(0, 0, -4) 55 | scene:setLight(0.2, 0.8, 0.4) 56 | 57 | 58 | -- add six copies of the shape to the scene 59 | 60 | n = scene:getRootNode() 61 | 62 | n1 = n:addChildNode() 63 | n1:addShape(shape, 2, 0, 0) 64 | 65 | n2 = n:addChildNode() 66 | n2:addShape(shape, -2, 0, 0) 67 | n2:setColorBias(0.8) 68 | 69 | n3 = n:addChildNode() 70 | n3:addShape(shape, 0, 2, 0) 71 | n3:setWireframeMode(2) 72 | n3:setWireframeColor(1) 73 | n3:setFilled(false) 74 | 75 | n4 = n:addChildNode() 76 | n4:addShape(shape, 0, -2, 0) 77 | n4:setWireframeMode(1) 78 | n4:setWireframeColor(1) 79 | n4:setFilled(false) 80 | 81 | n5 = n:addChildNode() 82 | n5:addShape(shape, 0, 0, 2) 83 | n5:setColorBias(0.8) 84 | n5:setWireframeMode(2) 85 | n5:setWireframeColor(0) 86 | 87 | n6 = n:addChildNode() 88 | n6:addShape(shape, 0, 0, -2) 89 | n6:setColorBias(-0.8) 90 | n6:setWireframeMode(1) 91 | n6:setWireframeColor(1) 92 | 93 | 94 | -- and we're ready to go! 95 | 96 | local gfx = playdate.graphics 97 | local x = 0 98 | local z = -4 99 | local dx = 0 100 | local dz = 0 101 | 102 | --playdate.startAccelerometer() 103 | 104 | rot1 = lib3d.matrix.newRotation(5,0,0,1) 105 | rot2 = lib3d.matrix.newRotation(3,0,1,0) 106 | 107 | function playdate.update() 108 | 109 | if dx ~= 0 or dz ~= 0 then 110 | x += dx / 10 111 | z += dz / 10 112 | scene:setCameraOrigin(-x, 0, z) 113 | end 114 | 115 | --local x, y = playdate.readAccelerometer() 116 | 117 | --n:setTransform(lib3d.matrix.newRotation(y*100,1,0,0)) 118 | --n:addTransform(lib3d.matrix.newRotation(x*100,0,1,0)) 119 | 120 | n:addTransform(rot2) 121 | n:addTransform(rot1) 122 | 123 | gfx.clear(gfx.kColorBlack) 124 | scene:draw() 125 | end 126 | 127 | function playdate.cranked() 128 | local a = playdate.getCrankPosition() * math.pi / 180 129 | scene:setCameraUp(math.sin(a), math.cos(a), 0) 130 | end 131 | 132 | function playdate.upButtonDown() dz += 1 end 133 | function playdate.upButtonUp() dz -= 1 end 134 | function playdate.downButtonDown() dz -= 1 end 135 | function playdate.downButtonUp() dz += 1 end 136 | function playdate.leftButtonDown() dx -= 1 end 137 | function playdate.leftButtonUp() dx += 1 end 138 | function playdate.rightButtonDown() dx += 1 end 139 | function playdate.rightButtonUp() dx -= 1 end 140 | -------------------------------------------------------------------------------- /colorknot/mini3d/scene.h: -------------------------------------------------------------------------------- 1 | // 2 | // scene.h 3 | // Extension 4 | // 5 | // Created by Dave Hayden on 10/7/15. 6 | // Copyright © 2015 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #ifndef scene_h 10 | #define scene_h 11 | 12 | #include 13 | #include "3dmath.h" 14 | #include "shape.h" 15 | 16 | struct FaceInstance 17 | { 18 | Point3D* p1; // pointers into shape's points array 19 | Point3D* p2; 20 | Point3D* p3; 21 | Point3D* p4; 22 | Vector3D normal; 23 | float colorBias; // added to lighting computation 24 | #if ENABLE_ORDERING_TABLE 25 | struct FaceInstance* next; 26 | #endif 27 | }; 28 | typedef struct FaceInstance FaceInstance; 29 | 30 | typedef enum 31 | { 32 | kRenderInheritStyle = 0, 33 | kRenderFilled = (1<<0), 34 | kRenderWireframe = (1<<1), 35 | kRenderWireframeBack = (1<<2), 36 | kRenderWireframeWhite = (1<<3) // 0 = black, 1 = white 37 | } RenderStyle; 38 | 39 | struct ShapeInstance 40 | { 41 | Shape3D* prototype; // pointer to original shape 42 | Matrix3D transform; 43 | struct ShapeInstance* next; 44 | 45 | // cached values from node tree update 46 | int nPoints; 47 | Point3D* points; 48 | int nFaces; 49 | FaceInstance* faces; 50 | Point3D center; 51 | float colorBias; 52 | RenderStyle renderStyle; 53 | unsigned int inverted : 1; // transformation flipped it across a plane, so we need to reverse backface check 54 | #if ENABLE_Z_BUFFER 55 | unsigned int useZBuffer : 1; 56 | #endif 57 | #if ENABLE_ORDERING_TABLE 58 | int orderTableSize; 59 | FaceInstance** orderTable; 60 | #endif 61 | }; 62 | 63 | typedef struct ShapeInstance ShapeInstance; 64 | typedef struct Scene3DNode Scene3DNode; 65 | 66 | struct Scene3DNode 67 | { 68 | Matrix3D transform; 69 | Scene3DNode* parentNode; 70 | int nChildren; 71 | Scene3DNode** childNodes; 72 | int shapeCount; 73 | ShapeInstance* shapes; 74 | float colorBias; 75 | RenderStyle renderStyle; 76 | unsigned int isVisible:1; 77 | unsigned int needsUpdate:1; 78 | #if ENABLE_Z_BUFFER 79 | unsigned int useZBuffer:1; 80 | float zmin; 81 | #endif 82 | }; 83 | 84 | void Scene3DNode_init(Scene3DNode* node); 85 | void Scene3DNode_setTransform(Scene3DNode* node, Matrix3D* xform); 86 | void Scene3DNode_addTransform(Scene3DNode* node, Matrix3D* xform); 87 | void Scene3DNode_addShape(Scene3DNode* node, Shape3D* shape); 88 | void Scene3DNode_addShapeWithOffset(Scene3DNode* node, Shape3D* shape, Vector3D offset); 89 | void Scene3DNode_addShapeWithTransform(Scene3DNode* node, Shape3D* shape, Matrix3D transform); 90 | Scene3DNode* Scene3DNode_newChild(Scene3DNode* node); 91 | void Scene3DNode_setColorBias(Scene3DNode* node, float bias); 92 | void Scene3DNode_setRenderStyle(Scene3DNode* node, RenderStyle style); 93 | RenderStyle Scene3DNode_getRenderStyle(Scene3DNode* node); 94 | void Scene3DNode_setVisible(Scene3DNode* node, int visible); 95 | #if ENABLE_Z_BUFFER 96 | void Scene3DNode_setUsesZBuffer(Scene3DNode* node, int flag); 97 | #endif 98 | 99 | typedef struct 100 | { 101 | unsigned int hasPerspective : 1; 102 | 103 | Matrix3D camera; 104 | Vector3D light; 105 | 106 | // location of the Z vanishing point on the screen. (0,0) is top left corner, (1,1) is bottom right. Defaults to (0.5,0.5) 107 | float centerx; 108 | float centery; 109 | 110 | // display scaling factor. Default is 120, so that the screen extents are (-1.66,1.66)x(-1,1) 111 | float scale; 112 | 113 | Scene3DNode root; 114 | 115 | // all shapes from the render tree are added here and z-sorted 116 | ShapeInstance** shapelist; 117 | int shapelistsize; 118 | 119 | #if ENABLE_Z_BUFFER 120 | float zmin; 121 | #endif 122 | 123 | } Scene3D; 124 | 125 | void Scene3D_init(Scene3D* scene); 126 | void Scene3D_deinit(Scene3D* scene); 127 | void Scene3D_setCamera(Scene3D* scene, Point3D origin, Point3D lookAt, float scale, Vector3D up); 128 | void Scene3D_setGlobalLight(Scene3D* scene, Vector3D light); 129 | Scene3DNode* Scene3D_getRootNode(Scene3D* scene); 130 | void Scene3D_draw(Scene3D* scene, uint8_t* buffer, int rowstride); 131 | void Scene3D_drawNode(Scene3D* scene, Scene3DNode* node, uint8_t* bitmap, int rowstride); 132 | void Scene3D_setCenter(Scene3D* scene, float x, float y); 133 | 134 | #if ENABLE_Z_BUFFER 135 | void Scene3D_getZMask(Scene3D* scene, uint8_t* zmask, int rowstride); 136 | #endif 137 | 138 | #endif /* scene_h */ 139 | -------------------------------------------------------------------------------- /resetbutton.py: -------------------------------------------------------------------------------- 1 | # safe_restart_shutdown_interrupt_Pi.py 2 | # 3 | # ----------------------------------------------------------------------------- 4 | # Raspberry Pi Safe Restart and Shutdown Python Script 5 | # ----------------------------------------------------------------------------- 6 | # WRITTEN BY: Ho Yun "Bobby" Chan 7 | # @ SparkFun Electronics 8 | # MODIFIED: 3/18/2021 9 | # DATE: 3/31/2020 10 | # 11 | # 12 | # Based on code from the following blog and tutorials: 13 | # 14 | # Kevin Godden 15 | # https://www.ridgesolutions.ie/index.php/2013/02/22/raspberry-pi-restart-shutdown-your-pi-from-python-code/ 16 | # 17 | # Pete Lewis 18 | # https://learn.sparkfun.com/tutorials/raspberry-pi-stand-alone-programmer#resources-and-going-further 19 | # 20 | # Shawn Hymel 21 | # https://learn.sparkfun.com/tutorials/python-programming-tutorial-getting-started-with-the-raspberry-pi/experiment-1-digital-input-and-output 22 | # 23 | # Ben Croston raspberry-gpio-python module 24 | # https://sourceforge.net/p/raspberry-gpio-python/wiki/Inputs/ 25 | # 26 | # ==================== DESCRIPTION ==================== 27 | # 28 | # This python script takes advantage of the Qwiic pHat v2.0's 29 | # built-in general purpose button to safely reboot/shutdown you Pi: 30 | # 31 | # 1.) If you press the button momentarily, the Pi will reboot. 32 | # 2.) Holding down the button for about 3 seconds the Pi will shutdown. 33 | # 34 | # This example also takes advantage of interrupts so that it uses a negligible 35 | # amount of CPU. This is more efficient since it isn't taking up all of the Pi's 36 | # processing power. 37 | # 38 | # ========== TUTORIAL ========== 39 | # For more information on running this script on startup, 40 | # check out the associated tutorial to adjust your "rc.local" file: 41 | # 42 | # https://learn.sparkfun.com/tutorials/raspberry-pi-safe-reboot-and-shutdown-button 43 | # 44 | # ========== PRODUCTS THAT USE THIS CODE ========== 45 | # 46 | # Feel like supporting our work? Buy a board from SparkFun! 47 | # 48 | # Qwiic pHAT v2.0 49 | # https://www.sparkfun.com/products/15945 50 | # 51 | # You can also use any button but you would need to wire it up 52 | # instead of stacking the pHAT on your Pi. 53 | # 54 | # LICENSE: This code is released under the MIT License (http://opensource.org/licenses/MIT) 55 | # 56 | # Distributed as-is; no warranty is given 57 | # 58 | # ----------------------------------------------------------------------------- 59 | 60 | import time 61 | import RPi.GPIO as GPIO #Python Package Reference: https://pypi.org/project/RPi.GPIO/ 62 | import subprocess 63 | 64 | # Pin definition 65 | reset_shutdown_pin = 26 66 | 67 | # Suppress warnings 68 | GPIO.setwarnings(False) 69 | 70 | # Use "GPIO" pin numbering 71 | GPIO.setmode(GPIO.BCM) 72 | 73 | # Use built-in internal pullup resistor so the pin is not floating 74 | # if using a momentary push button without a resistor. 75 | GPIO.setup(reset_shutdown_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP) 76 | 77 | def runcommand(command): 78 | process = subprocess.Popen(command.split(), stdout=subprocess.PIPE) 79 | output = process.communicate()[0] 80 | print(output) 81 | 82 | # modular function to restart Pi 83 | def restart_mirror(): 84 | print("restarting Mirror") 85 | runcommand("/usr/bin/sudo killall mirror") 86 | 87 | # modular function to shutdown Pi 88 | def restart_pi(): 89 | print("shutting down") 90 | runcommand("/usr/bin/sudo killall runmirror.sh") 91 | runcommand("/usr/bin/sudo killall mirror") 92 | runcommand("/usr/bin/sudo /sbin/shutdown -r now") 93 | 94 | while True: 95 | #short delay, otherwise this code will take up a lot of the Pi's processing power 96 | time.sleep(0.5) 97 | 98 | # wait for a button press with switch debounce on the falling edge so that this script 99 | # is not taking up too many resources in order to shutdown/reboot the Pi safely 100 | channel = GPIO.wait_for_edge(reset_shutdown_pin, GPIO.FALLING, bouncetime=200) 101 | 102 | if channel is None: 103 | print('Timeout occurred') 104 | else: 105 | print('Edge detected on channel', channel) 106 | 107 | # For troubleshooting, uncomment this line to output button status on command line 108 | #print('GPIO state is = ', GPIO.input(reset_shutdown_pin)) 109 | counter = 0 110 | 111 | while GPIO.input(reset_shutdown_pin) == False: 112 | # For troubleshooting, uncomment this line to view the counter. If it reaches a value above 4, we will restart. 113 | #print(counter) 114 | counter += 1 115 | time.sleep(0.5) 116 | 117 | # long button press 118 | if counter > 4: 119 | restart_pi() 120 | 121 | #if short button press, restart mirror 122 | restart_mirror() 123 | -------------------------------------------------------------------------------- /mirrorpi/ringbuffer.c: -------------------------------------------------------------------------------- 1 | // 2 | // ringbuffer.c 3 | // Playdate Simulator 4 | // 5 | // Created by Dave Hayden on 5/21/18. 6 | // Copyright © 2018 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | #include "ringbuffer.h" 13 | 14 | void RingBuffer_init(RingBuffer* r) 15 | { 16 | r->buffer = NULL; 17 | r->bufferlen = 0; 18 | // r->fileOffset = 0; 19 | r->inpos = 0; 20 | r->outpos = 0; 21 | } 22 | 23 | void RingBuffer_deinit(RingBuffer* r) 24 | { 25 | if ( r->buffer != NULL ) 26 | free(r->buffer); 27 | } 28 | 29 | bool RingBuffer_setSize(RingBuffer* r, unsigned int length, unsigned int datasize) 30 | { 31 | r->datasize = datasize; 32 | 33 | if ( r->bufferlen == length ) 34 | return true; 35 | 36 | if ( length % datasize != 0 ) 37 | length += 4 - (length % datasize); 38 | 39 | void* buf = realloc(r->buffer, length); 40 | 41 | if ( buf == NULL ) 42 | return 0; 43 | 44 | memset(buf, 0, length); 45 | r->buffer = buf; 46 | r->inpos = 0; 47 | r->outpos = 0; 48 | r->bufferlen = length; 49 | 50 | return false; 51 | } 52 | 53 | void RingBuffer_reset(RingBuffer* r) 54 | { 55 | r->outpos = 0; 56 | r->inpos = 0; 57 | // r->fileOffset = 0; 58 | } 59 | 60 | unsigned int RingBuffer_getSize(RingBuffer* r) 61 | { 62 | return r->bufferlen; 63 | } 64 | 65 | // XXX - This isn't quite right, should handle case where outpos = 0 66 | unsigned int RingBuffer_getFreeSpace(RingBuffer* r) 67 | { 68 | unsigned int o = r->outpos; 69 | unsigned int i = r->inpos; 70 | 71 | if ( i < o ) 72 | return o - i - r->datasize; 73 | else 74 | return o - r->datasize + r->bufferlen - i; 75 | } 76 | 77 | /* 78 | unsigned int RingBuffer_getOutputOffset(RingBuffer* r) 79 | { 80 | return r->fileOffset + r->outpos; 81 | } 82 | 83 | void RingBuffer_setOutputOffset(RingBuffer* r, unsigned int offset) 84 | { 85 | r->fileOffset = offset - r->outpos; 86 | } 87 | */ 88 | 89 | unsigned int RingBuffer_getInputAvailableSize(RingBuffer* r) 90 | { 91 | // save r->outpos in case another thread changes it 92 | unsigned int o = r->outpos; 93 | 94 | if ( o <= r->inpos ) 95 | return r->bufferlen - r->inpos; 96 | else 97 | return o - r->inpos - r->datasize; 98 | } 99 | 100 | void* RingBuffer_getInputPointer(RingBuffer* r) 101 | { 102 | return (uint8_t*)r->buffer + r->inpos; 103 | } 104 | 105 | void RingBuffer_moveInputPointer(RingBuffer* r, unsigned int bytes) 106 | { 107 | if ( r->bufferlen == 0 ) 108 | printf("what??\n"); 109 | 110 | r->inpos = (r->inpos + bytes) % r->bufferlen; 111 | 112 | if ( r->inpos == r->bufferlen && r->outpos > 0 ) 113 | r->inpos = 0; 114 | } 115 | 116 | #if !defined(MIN) 117 | #define MIN(a,b) (((a)<(b))?(a):(b)) 118 | #endif 119 | 120 | unsigned int RingBuffer_addData(RingBuffer* r, const void* ptr, unsigned int length) 121 | { 122 | int c = (r->inpos >= r->outpos) ? 2 : 1; 123 | unsigned int l = length; 124 | const uint8_t* data = ptr; 125 | 126 | while ( c-- > 0 && l > 0 ) 127 | { 128 | unsigned int n = MIN(l, RingBuffer_getInputAvailableSize(r)); 129 | memcpy(RingBuffer_getInputPointer(r), data, n); 130 | RingBuffer_moveInputPointer(r, n); 131 | data += n; 132 | l -= n; 133 | } 134 | 135 | return length - l; 136 | } 137 | 138 | unsigned int RingBuffer_getBytesAvailable(RingBuffer* r) 139 | { 140 | // save r->inpos in case another thread changes it 141 | unsigned int i = r->inpos; 142 | unsigned int o = r->outpos; 143 | 144 | if ( o <= i ) 145 | return i - o; 146 | else 147 | return r->bufferlen - o + i; 148 | } 149 | 150 | unsigned int RingBuffer_getOutputAvailableSize(RingBuffer* r) 151 | { 152 | unsigned int i = r->inpos; 153 | 154 | if ( r->outpos <= i ) 155 | return i - r->outpos; 156 | else 157 | return r->bufferlen - r->outpos; 158 | } 159 | 160 | void* RingBuffer_getOutputPointer(RingBuffer* r) 161 | { 162 | return (uint8_t*)r->buffer + r->outpos; 163 | } 164 | 165 | void RingBuffer_moveOutputPointer(RingBuffer* r, unsigned int bytes) 166 | { 167 | r->outpos = (r->outpos + bytes) % r->bufferlen; 168 | 169 | /* no! we can't touch inpos! 170 | if ( r->outpos == r->inpos ) 171 | { 172 | r->outpos = r->inpos = 0; 173 | return; 174 | } 175 | */ 176 | // wrap inpos around if it's waiting for outpos to get off the 0 position 177 | if ( r->inpos == r->bufferlen && r->outpos != 0 ) 178 | r->inpos = 0; 179 | 180 | if ( r->outpos == r->bufferlen && r->inpos != 0 ) 181 | { 182 | // r->fileOffset += r->bufferlen; 183 | r->outpos = 0; 184 | } 185 | } 186 | 187 | unsigned int RingBuffer_readData(RingBuffer* r, void* ptr, unsigned int length) 188 | { 189 | // printf("RingBuffer_readData(%p, %p, %i)\n", r, ptr, length); 190 | 191 | int c = (r->outpos > r->inpos) ? 2 : 1; 192 | unsigned int l = length; 193 | uint8_t* data = ptr; 194 | 195 | while ( c-- > 0 && l > 0 ) 196 | { 197 | unsigned int n = MIN(l, RingBuffer_getOutputAvailableSize(r)); 198 | memcpy(data, RingBuffer_getOutputPointer(r), n); 199 | RingBuffer_moveOutputPointer(r, n); 200 | data += n; 201 | l -= n; 202 | } 203 | 204 | return length - l; 205 | } 206 | -------------------------------------------------------------------------------- /crank/crank.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | bool i2cread(int addr, uint8_t* buf, int count) 4 | { 5 | int n = Wire.requestFrom(addr, count); 6 | 7 | if ( n < count ) 8 | return false; 9 | 10 | int i; 11 | 12 | for ( i = 0; i < count && Wire.available(); ++i ) 13 | { 14 | buf[i] = Wire.read(); 15 | // Serial.print(buf[i], HEX); 16 | // Serial.print(" "); 17 | } 18 | 19 | if ( i < count ) 20 | return false; 21 | 22 | // Serial.println(""); 23 | 24 | return true; 25 | } 26 | 27 | bool i2cwrite(int addr, uint8_t* buf, int count) 28 | { 29 | bool ret; 30 | 31 | Wire.beginTransmission(addr); 32 | ret = (Wire.write(0x00) == 1 && Wire.write(buf, count) == count); 33 | Wire.endTransmission(); 34 | 35 | return ret; 36 | } 37 | 38 | 39 | bool tryCrank(int addr) 40 | { 41 | uint8_t buf[10]; 42 | 43 | if ( !i2cread(addr, buf, 10) ) 44 | { 45 | Serial.println("i2cread failed"); 46 | return false; 47 | } 48 | 49 | buf[0] = (buf[7] & 0x78) | 0x01; 50 | buf[1] = buf[8]; 51 | buf[2] = (buf[9] & 0x1f) | 0x40; 52 | 53 | if ( !i2cwrite(addr, buf, 3) ) 54 | { 55 | Serial.println("write failed"); 56 | return false; 57 | } 58 | 59 | if ( !i2cread(addr, buf, 10) ) 60 | { 61 | Serial.println("i2cread #2 failed"); 62 | return false; 63 | } 64 | 65 | return true; 66 | } 67 | 68 | 69 | #define CRANK1_POWER 3 70 | #define CRANK2_POWER 2 71 | 72 | #define CRANK1_ADDR 0x5e //if SDA high at boot 73 | #define CRANK2_ADDR 0x1f //if SDA low at boot 74 | 75 | void initCranks() 76 | { 77 | // crank 1 is on xiao's 3v3 line, and SDA is on a pullup, so it'll have address 0x5e 78 | // then we set SDA low, turn on crank 2, and it should have address 0x1f 79 | 80 | pinMode(CRANK2_POWER, OUTPUT); 81 | digitalWrite(CRANK2_POWER, LOW); 82 | 83 | pinMode(4,OUTPUT); 84 | digitalWrite(4, LOW); // set SDA low 85 | 86 | delay(10); 87 | 88 | digitalWrite(CRANK2_POWER, HIGH); // turn on crank 2 89 | delay(1); // at least 200 uS for address set 90 | 91 | // set SDA and SCL to inputs 92 | pinMode(4,INPUT); 93 | pinMode(5,INPUT); 94 | 95 | pinMode(CRANK1_POWER,OUTPUT); 96 | digitalWrite(CRANK1_POWER, HIGH); 97 | 98 | delay(100); 99 | // init i2c 100 | Wire.begin(); 101 | Serial.println("wire.begin() begun"); 102 | 103 | Serial.println("looking for cranks.."); 104 | 105 | if ( tryCrank(CRANK1_ADDR) ) 106 | Serial.println("crank 1 (0x53) found"); 107 | else 108 | Serial.println("crank 1 (0x53) not found :( :( :("); 109 | 110 | if ( tryCrank(CRANK2_ADDR) ) 111 | Serial.println("crank 2 (0x1f) found"); 112 | else 113 | Serial.println("crank 2 (0x1f) not found :( :( :("); 114 | } 115 | 116 | void setup() 117 | { 118 | Serial1.begin(115200); 119 | Serial.begin(9600); 120 | delay(2000); // 121 | 122 | initCranks(); 123 | } 124 | 125 | struct crank 126 | { 127 | int addr; 128 | float startangle; 129 | float lastangle; 130 | float xavg; 131 | float yavg; 132 | bool handlepresent; 133 | }; 134 | 135 | struct crank crank1 = { .addr = CRANK1_ADDR, .startangle = -1 }; 136 | struct crank crank2 = { .addr = CRANK2_ADDR, .startangle = -1 }; 137 | 138 | #define CRANK_SMOOTH_THRESHOLD 10 // if x or y changes more than this amount, don't smooth value 139 | #define CRANK_SMOOTHING 10 // but for smaller movement we need to average out the noise 140 | 141 | // below this amount of angle change, system reports no change 142 | #define CRANK_THRESHOLD 0.8f 143 | 144 | // rate limit our reporting so we don't overwhelm the poor Pi 145 | #define REPORT_PERIOD 50 146 | 147 | bool sampleCrankSensor(struct crank* crank) 148 | { 149 | int8_t buf[5]; 150 | 151 | #define ABS(x) (((x)>=0)?(x):-(x)) 152 | #define MAX(x,y) (((x)>(y))?(x):(y)) 153 | 154 | if ( !i2cread(crank->addr, (uint8_t*)buf, 5) ) 155 | return false; 156 | 157 | int y = ((int)buf[1] << 4) | (buf[4] & 0xf); 158 | int x = ((int)buf[0] << 4) | (((uint8_t)buf[4] >> 4) & 0xf); 159 | 160 | if ( ABS(y) <= 20 && ABS(x) <= 20 ) 161 | { 162 | crank->handlepresent = false; 163 | return true; 164 | } 165 | 166 | if ( ABS(x-crank->xavg) < CRANK_SMOOTH_THRESHOLD && ABS(y-crank->yavg) < CRANK_SMOOTH_THRESHOLD ) 167 | { 168 | crank->xavg += (x - crank->xavg) / CRANK_SMOOTHING; 169 | crank->yavg += (y - crank->yavg) / CRANK_SMOOTHING; 170 | } 171 | else 172 | { 173 | crank->xavg = x; 174 | crank->yavg = y; 175 | } 176 | 177 | crank->handlepresent = true; 178 | return true; 179 | } 180 | 181 | static int count = 0; 182 | 183 | 184 | void updateCrank(struct crank* crank) 185 | { 186 | if ( crank->handlepresent ) 187 | { 188 | float angle = atan2(crank->yavg,-crank->xavg) * 180 / M_PI; 189 | 190 | if ( angle < 0 ) angle += 360; 191 | 192 | if ( crank->startangle == -1 ) 193 | { 194 | // XXX - need to debounce this 195 | Serial.println("handle in"); 196 | Serial1.println("in"); 197 | crank->startangle = angle; 198 | crank->lastangle = 0; 199 | } 200 | 201 | angle -= crank->startangle; 202 | if ( angle < 0 ) angle += 360; 203 | 204 | float change = angle - crank->lastangle; 205 | 206 | if ( change > 180 ) 207 | change -= 360; 208 | else if ( change < -180 ) 209 | change += 360; 210 | 211 | if ( ABS(change) > CRANK_THRESHOLD ) 212 | { 213 | Serial.println(angle, 1); 214 | Serial1.println(angle, 1); 215 | crank->lastangle = angle; 216 | } 217 | } 218 | else if ( crank->startangle != -1 ) 219 | { 220 | Serial.println("handle out"); 221 | Serial1.println("out"); 222 | crank->startangle = -1; 223 | } 224 | } 225 | 226 | void loop() 227 | { 228 | bool handlepresent; 229 | 230 | if ( !sampleCrankSensor(&crank1) ) Serial.println("crank 1 not found"); 231 | if ( !sampleCrankSensor(&crank2) ) Serial.println("crank 2 not found"); 232 | 233 | if ( ++count == REPORT_PERIOD ) 234 | { 235 | count = 0; 236 | updateCrank(&crank1); 237 | updateCrank(&crank2); 238 | } 239 | /* 240 | Serial.print(crank1.lastangle); 241 | Serial.print(" "); 242 | Serial.println(crank2.lastangle); 243 | */ 244 | } 245 | -------------------------------------------------------------------------------- /mirrorpi/main.c: -------------------------------------------------------------------------------- 1 | // 2 | // main.c 3 | // MirrorJr 4 | // 5 | // Created by Dave Hayden on 9/25/24. 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "SDL.h" 19 | 20 | #include "frame.h" 21 | #include "audio.h" 22 | #include "stream.h" 23 | #include "serial.h" 24 | #include "controls.h" 25 | 26 | bool checkExit() 27 | { 28 | SDL_Event event; 29 | 30 | if ( SDL_PollEvent(&event) ) 31 | { 32 | if ( event.type == SDL_QUIT ) 33 | return true; 34 | // else if ( event.type != SDL_WINDOWEVENT ) 35 | // printf("unhandled event type 0x%x\n", event.type); 36 | } 37 | 38 | return false; 39 | } 40 | 41 | void* copySerialToRingbuffer(void* ud); 42 | 43 | int bytesread = 0; 44 | time_t starttime; 45 | bool serial_running = false; 46 | 47 | /* 48 | static void droproot() 49 | { 50 | const char* username = "dave"; 51 | struct passwd *pw = getpwnam(username); 52 | 53 | if (initgroups(pw->pw_name, pw->pw_gid) != 0 || 54 | setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0) { 55 | fprintf(stderr, "droproot: Couldn't change to '%.32s' uid=%lu gid=%lu: %s\n", 56 | username, 57 | (unsigned long)pw->pw_uid, 58 | (unsigned long)pw->pw_gid, 59 | strerror(errno)); 60 | exit(1); 61 | } 62 | } 63 | */ 64 | 65 | int get_ip_address(char *ip_buffer); 66 | 67 | int main(int argc, const char * argv[]) 68 | { 69 | if ( SDL_InitSubSystem(SDL_INIT_VIDEO) != 0 ) 70 | { 71 | printf("video init failed: %s", SDL_GetError()); 72 | return false; 73 | } 74 | 75 | /* 76 | int numModes = SDL_GetNumDisplayModes(0); 77 | for ( int i = 0; i < numModes; ++i ) 78 | { 79 | SDL_DisplayMode mode; 80 | 81 | if ( SDL_GetDisplayMode(0, i, &mode) == 0 ) 82 | printf("mode %i: fmt=%x w=%i h=%i refresh=%i\n", i, mode.format, mode.w, mode.h, mode.refresh_rate); 83 | else 84 | printf("Couldn't get display mode %i\n", i); 85 | } 86 | */ 87 | 88 | #if TARGET_MACOS 89 | SDL_Window* window = SDL_CreateWindow("Playdate", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1200, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN); 90 | #else 91 | SDL_Window* window = SDL_CreateWindow("Playdate", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN); 92 | #endif 93 | 94 | if ( window == NULL ) 95 | { 96 | printf("couldn't create window: %s\n", SDL_GetError()); 97 | return -1; 98 | } 99 | 100 | SDL_ShowCursor(SDL_DISABLE); 101 | 102 | if ( !controls_init() ) 103 | { 104 | printf("error initializing control i/o\n"); 105 | return -1; 106 | } 107 | 108 | if ( !stream_init() ) 109 | { 110 | printf("error initializing stream input\n"); 111 | return -1; 112 | } 113 | 114 | if ( !frame_init(window) ) 115 | return -1; 116 | 117 | audio_init(); 118 | //droproot(); 119 | 120 | uint8_t ipaddr[INET_ADDRSTRLEN] = "...."; 121 | get_ip_address((char*)ipaddr); 122 | frame_showWaitScreen(ipaddr); 123 | 124 | for ( ;; ) 125 | { 126 | printf("waiting for playdate..\n"); 127 | 128 | frame_showWaitScreen(ipaddr); 129 | 130 | printf("checking serial port\n"); 131 | 132 | while ( !ser_isOpen() ) 133 | { 134 | // printf("checking exit signal\n"); 135 | 136 | if ( checkExit() ) 137 | return 0; 138 | 139 | usleep(10000); 140 | 141 | // printf("calling ser_open()\n"); 142 | ser_open(); 143 | } 144 | 145 | printf("connected!\n"); 146 | stream_begin(); 147 | 148 | pthread_attr_t attrs; 149 | pthread_attr_init(&attrs); 150 | 151 | // struct sched_param sched = {.sched_priority=50}; 152 | // pthread_attr_setschedparam(&attrs, &sched); 153 | 154 | serial_running = true; 155 | 156 | pthread_t readthread; 157 | pthread_create(&readthread, &attrs, copySerialToRingbuffer, NULL); 158 | 159 | pthread_attr_destroy(&attrs); 160 | 161 | time_t lastpoke = 0; 162 | starttime = time(NULL); 163 | 164 | while ( ser_isOpen() ) 165 | { 166 | if ( checkExit() ) 167 | return 0; 168 | 169 | time_t now = time(NULL); 170 | 171 | if ( now > lastpoke ) 172 | { 173 | stream_poke(); 174 | lastpoke = now; 175 | 176 | if ( now > starttime ) 177 | ; //printf("%i bytes read, %f kB/s\n", bytesread, (float)bytesread/(now-starttime)/1024.0f); 178 | } 179 | 180 | stream_process(); 181 | controls_scan(); 182 | usleep(1000); 183 | } 184 | 185 | audio_stop(); 186 | stream_reset(); 187 | 188 | serial_running = false; 189 | pthread_join(readthread, NULL); 190 | } 191 | 192 | return 0; 193 | } 194 | 195 | void* copySerialToRingbuffer(void* ud) 196 | { 197 | printf("serial monitor started..\n"); 198 | 199 | //FILE* log = fopen("log.bin", "w"); 200 | 201 | while ( serial_running ) 202 | { 203 | static uint8_t buf[65536]; 204 | ssize_t n = ser_read(buf, sizeof(buf)); 205 | 206 | if ( n > 0 ) 207 | { 208 | // fwrite(buf, 1, (size_t)n, log); 209 | // fflush(log); 210 | 211 | bytesread += n; 212 | stream_addData(buf, (unsigned)n); 213 | } 214 | else if ( n < 0 ) 215 | { 216 | printf("ser_read returned %i errno=%i\n", n, errno); 217 | break; 218 | } 219 | } 220 | 221 | printf("serial monitor ended..\n"); 222 | return NULL; 223 | } 224 | 225 | #include 226 | #include 227 | #include 228 | #include 229 | #include 230 | 231 | int get_ip_address(char *ip_buffer) 232 | { 233 | int fd; 234 | struct ifreq ifr; 235 | 236 | // Create a socket to retrieve information about the interface 237 | fd = socket(AF_INET, SOCK_DGRAM, 0); 238 | if (fd == -1) { 239 | perror("Socket creation failed"); 240 | return -1; 241 | } 242 | 243 | strncpy(ifr.ifr_name, "wlan0", IFNAMSIZ-1); 244 | ifr.ifr_name[IFNAMSIZ-1] = '\0'; 245 | 246 | // Perform an ioctl call to get the IP address of the interface 247 | if (ioctl(fd, SIOCGIFADDR, &ifr) == -1) { 248 | perror("ioctl failed"); 249 | close(fd); 250 | return -1; 251 | } 252 | 253 | // Convert the IP address to a string and store it in ip_buffer 254 | struct sockaddr_in *ip_addr = (struct sockaddr_in *)&ifr.ifr_addr; 255 | strncpy(ip_buffer, inet_ntoa(ip_addr->sin_addr), INET_ADDRSTRLEN); 256 | 257 | // Clean up and return success 258 | close(fd); 259 | return 0; 260 | } 261 | -------------------------------------------------------------------------------- /colorknot/_lib3d.md: -------------------------------------------------------------------------------- 1 | 2 | ## lib3d - a 3D rendering Lua extension for Playdate 3 | 4 | The lib3D extension is a 3D scene rendered, and an example of integrating C code into Lua. It implements a simple tree-based scene with triangle- or quad-faced shapes at the leaf nodes. By default there is no depth sorting; entire shapes are rendered one at a time using the painter's algorithm and backface culling to eliminate hidden faces, but a a buffer or an ordering table can be included through compile-time defines. No clipping is performed, either between shapes/faces or a clipping plane--if any of the vertices in a face is behind the camera, the face isn't rendered. Draw styles can be set per tree branch, to enable/disable filled faces (fill patterns are currently hardcoded in the source) and wireframe edges. 5 | 6 | **Compile-time options (see mini3d.h):** 7 | 8 | `#define ENABLE_Z_BUFFER 1` 9 | 10 | Implements a 16-bit depth buffer. Use this when you have intersecting geometry. 11 | 12 | `#define ENABLE_ORDERING_TABLE 1` 13 | 14 | Ordering tables are a quick and dirty way to sort faces in a shape so it draws correctly via the painter's algorithm (rendering faces from back to front). Generally, you'd start with a low table size (see shape:setOrderTableSize()) and increase it until the shape is drawn correctly. 15 | 16 | 17 | ### lib3d.scene 18 | 19 | **`lib3d.scene.new()`** 20 | 21 | Creates a new 3D scene 22 | 23 | **`lib3d.scene:draw()`** 24 | 25 | Draws the scene into the current frame buffer 26 | 27 | **`lib3d.scene:drawNode(n)`** 28 | 29 | Draws the given node into the current frame buffer 30 | 31 | **`lib3d.scene:getRootNode()`** 32 | 33 | Returns the root node of the render tree, a lib3d.scenenode object 34 | 35 | **`lib3d.scene:setCenter(x, y)`** 36 | 37 | Sets the position of the distant vanishing point, in fractional screen coordinates. The default is (0.5, 0.5), meaning as an object recedes into the distance along the Z axis (when the camera target is the default (0, 0, 1) vector) it moves to the center of the screen 38 | 39 | **`lib3d.scene:setCameraOrigin(x, y, z)`** 40 | 41 | Sets the position of the camera 42 | 43 | **`lib3d.scene:setCameraScale(s)`** 44 | 45 | Sets the final scaling factor of the camera, akin to focal length. At default value 1.0, the extents of the screen (with default center) are [-1.6,1.6] x [-1,1]. 46 | 47 | **`lib3d.scene:setCameraTarget(x, y, z)`** 48 | 49 | Points the camera at point (x,y,z) in 3D space 50 | 51 | **`lib3d.scene:setCameraUp(x, y, z)`** 52 | 53 | Rolls the camera so that vector (x,y,z) is pointing up 54 | 55 | **`lib3d.scene:setLight(x, y, z)`** 56 | 57 | Sets the direction of the global light source 58 | 59 | 60 | ### lib3d.scenenode 61 | 62 | **`lib3d.scenenode:addChildNode()`** 63 | 64 | Adds (and returns) a child node to the given scene node 65 | 66 | **`lib3d.scenenode:addShape(s, [m])`** 67 | **`lib3d.scenenode:addShape(s, [x, y, z])`** 68 | 69 | Adds the given libd3.shape to the node, with optional offset from the center of the node (x,y,z) or transformation matrix m. 70 | 71 | **`lib3d.scenenode:addTransform(m)`** 72 | 73 | Appends the given lib3d.matrix to the node's transform matrix 74 | 75 | **`lib3d.scenenode:setTransform(m)`** 76 | 77 | Replaces the node's transformation matrix with the given lib3d.matrix 78 | 79 | **`lib3d.scenenode:translateBy(dx,dy,dz)`** 80 | 81 | Appends a translation to the node's transform matrix 82 | 83 | **`lib3d.scenenode:scaleBy(sx [,sy,sz])`** 84 | 85 | Appends a scaling to the node's transform matrix. If only one argument is given, all axes are scaled by that amount. 86 | 87 | **`lib3d.scenenode:setColorBias(bias)`** 88 | 89 | Adds the given color bias to the node, lightening or darkening all shapes in the render tree underneath this node 90 | 91 | **`lib3d.scenenode:setFilled(flag)`** 92 | 93 | Turns face fills on or off. Default is filled. 94 | 95 | **`lib3d.scenenode:setWireframeMode(mode)`** 96 | 97 | If mode is 0 (default), no wireframe is drawn. If mode is 1, only the front-facing edges are drawn. If mode is 2, all edges are drawn. 98 | 99 | **`lib3d.scenenode:setWireframeColor(color)`** 100 | 101 | Sets the color of the edge drawing; 1 = white, 0 = black 102 | 103 | **`lib3d.scenenode:setVisible(flag)`** 104 | 105 | Turns rendering on and off for this branch of the render tree. Default is on, natch. 106 | 107 | 108 | ### lib3d.matrix 109 | 110 | **`lib3d.matrix.new(m00, m01, m02, m10, m11, m12, m20, m21, m22, dx, dy, dz)`** 111 | 112 | Returns a 3D affine transformation matrix with the given parameters 113 | 114 | **`lib3d.matrix.newRotation(angle, dx, dy, dz)`** 115 | 116 | Returns a transformation matrix representing a rotation of angle degrees around the vector (dx,dy,dx) 117 | 118 | **`lib3d.matrix:addTranslation(dx, dy, dz)`** 119 | 120 | Adds the given translation to the matrix 121 | 122 | **`lib3d.matrix:__mul(m)`** 123 | 124 | Metamethod allowing matrix composition with the * operator 125 | 126 | 127 | ### lib3d.point 128 | 129 | **`lib3d.point.new(x, y, z)`** 130 | 131 | Returns an object representing the point (x,y,z), used in constructing shapes 132 | 133 | **`lib3d.point:__index(k)`** 134 | **`lib3d.point:__newindex(k,v)`** 135 | 136 | Metamethod providing get and set for the point's x, y, and z coordinates 137 | 138 | **`lib3d.point:__mul(m)`** 139 | 140 | Metamethod for multiplying the point my matrix m 141 | 142 | 143 | ### lib3d.shape 144 | 145 | **`lib3d.shape.new()`** 146 | 147 | Creates a new shape object. Shapes are added to any number of lib3d.scenenode objects via the lib3d.scenenode:addShape() function. 148 | 149 | **`lib3d.shape:addFace(p1, p2, p3, [p4], [colorBias])`** 150 | 151 | Adds a new triangular or quadrilateral face to the shape, with points p1, p2, p3 as the triangle's vertices or p1, p2, p3, and p4 for the quad. Note: order matters! If the shape is marked closed, back faces will be culled during drawing so the vertices must be given in counter-clockwise order, as seen from outside the shape. The optional colorBias argument is used to make the face brighter or darker. 152 | 153 | **`lib3d.shape:setClosed(flag)`** 154 | 155 | Marks the shape closed, allowing the renderer to skip faces with normals pointing away from the camera. 156 | 157 | **`lib3d.shape:setOrderTableSize(bins)`** 158 | 159 | Only available when compiled with the ENABLE_ORDERING_TABLE compile-time option set. Renders the shape using the given ordering table size. 160 | -------------------------------------------------------------------------------- /mirrorpi/frame.c: -------------------------------------------------------------------------------- 1 | // 2 | // frame.c 3 | // MirrorJr 4 | // 5 | // Created by Dave Hayden on 9/25/24. 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "frame.h" 13 | #include "constants.h" 14 | 15 | //#define LOG printf 16 | #define LOG(s) 17 | 18 | SDL_Renderer* renderer; 19 | SDL_Texture* sdl_texture = NULL; 20 | uint8_t* framebuffer1bit; 21 | unsigned int* framebuffer32bit; // ARGB data 22 | 23 | #define DISPLAY_BLACK 0xff000000 24 | #define DISPLAY_WHITE 0xffb1afa8 25 | 26 | enum FrameMode 27 | { 28 | kFrame1bit, 29 | kFrame4bit_2x2, 30 | } mode = kFrame1bit; 31 | 32 | uint32_t palette[16] = { 33 | DISPLAY_BLACK, DISPLAY_WHITE 34 | }; 35 | 36 | int render_w = LCD_COLUMNS; 37 | int render_h = LCD_ROWS; 38 | 39 | bool frame_init(SDL_Window* window) 40 | { 41 | renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); 42 | 43 | if ( renderer == NULL ) 44 | { 45 | printf("couldn't create renderer: %s\n", SDL_GetError()); 46 | return 4; 47 | } 48 | 49 | SDL_RendererInfo info; 50 | 51 | if ( SDL_GetRendererInfo(renderer, &info) != 0 ) 52 | { 53 | printf("SDL_GetRendererInfo failed: %s", SDL_GetError()); 54 | return false; 55 | } 56 | 57 | if ( SDL_InitSubSystem(SDL_INIT_VIDEO) != 0 ) 58 | { 59 | printf("video init failed: %s", SDL_GetError()); 60 | return false; 61 | } 62 | 63 | framebuffer32bit = calloc(1, LCD_ROWS * LCD_COLUMNS * 4); 64 | framebuffer1bit = calloc(1, LCD_ROWSIZE * LCD_COLUMNS); 65 | 66 | assert(sdl_texture == NULL); 67 | 68 | sdl_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STREAMING, render_w, render_h); 69 | assert(sdl_texture); 70 | SDL_SetTextureBlendMode(sdl_texture, SDL_BLENDMODE_BLEND); 71 | 72 | return true; 73 | } 74 | 75 | // input buffer is LCD_COLUMNS x LCD_ROWS with LCD_ROWSIZE bytes per row 76 | // output buffer is render_w x render_h and render_w * 4 bytes per row 77 | 78 | static void convertTo32Bit_1bit(const uint8_t* in, uint32_t* out) 79 | { 80 | for ( int y = 0; y < LCD_ROWS; ++y ) 81 | { 82 | for ( int x = 0; x < LCD_COLUMNS / 8; ++x ) 83 | { 84 | unsigned char src_bits = in[x + y * LCD_ROWSIZE]; 85 | 86 | for ( int bit = 0; bit < 8; bit++ ) 87 | { 88 | unsigned char bitmask = 0x80 >> bit; 89 | unsigned int dest_color = (src_bits & bitmask) ? palette[1] : palette[0]; 90 | int dest_x = (x * 8) + bit; 91 | int out_i = dest_x + y * render_w; 92 | out[out_i] = dest_color; 93 | } 94 | } 95 | } 96 | } 97 | 98 | static void convertTo32Bit_4bit_2x2(const uint8_t* in, uint32_t* out) 99 | { 100 | for ( int y = 0; y < LCD_ROWS; y += 2 ) 101 | { 102 | for ( int x = 0; x < LCD_COLUMNS / 8; ++x ) 103 | { 104 | unsigned char src_bits1 = in[x + y * LCD_ROWSIZE]; 105 | unsigned char src_bits2 = in[x + (y+1) * LCD_ROWSIZE]; 106 | 107 | for ( int bit = 0; bit < 8; bit += 2 ) 108 | { 109 | unsigned char bitmask1 = 0x80 >> bit; 110 | unsigned char bitmask2 = 0x80 >> (bit+1); 111 | 112 | unsigned int idx = 113 | ((src_bits1 & bitmask1) ? 8 : 0) | 114 | ((src_bits1 & bitmask2) ? 4 : 0) | 115 | ((src_bits2 & bitmask1) ? 2 : 0) | 116 | ((src_bits2 & bitmask2) ? 1 : 0); 117 | 118 | unsigned int dest_color = palette[idx]; 119 | 120 | int dest_x = (x * 8) + bit; 121 | int out_i = dest_x + y * render_w; 122 | 123 | // instead of switching resolution, we'll draw 2x2 124 | out[out_i] = dest_color; 125 | out[out_i+1] = dest_color; 126 | out[out_i+render_w] = dest_color; 127 | out[out_i+render_w+1] = dest_color; 128 | } 129 | } 130 | } 131 | } 132 | 133 | void frame_reset() 134 | { 135 | mode = kFrame1bit; 136 | palette[0] = DISPLAY_BLACK; 137 | palette[1] = DISPLAY_WHITE; 138 | } 139 | 140 | void frame_set1BitPalette(RGB rgb[2]) 141 | { 142 | mode = kFrame1bit; 143 | 144 | for ( int i = 0; i < 2; ++i ) 145 | palette[i] = 0xff000000 | ((int)rgb[i].r << 16) | ((int)rgb[i].g << 8) | rgb[i].r; 146 | } 147 | 148 | void frame_set4BitPalette(RGB rgb[16]) 149 | { 150 | mode = kFrame4bit_2x2; 151 | 152 | for ( int i = 0; i < 16; ++i ) 153 | palette[i] = 0xff000000 | ((uint32_t)rgb[i].r << 16) | ((uint32_t)rgb[i].g << 8) | rgb[i].b; 154 | } 155 | 156 | void frame_setMode(enum FrameMode inmode, uint32_t pal[16]) 157 | { 158 | mode = inmode; 159 | memcpy(&palette[0], &pal[0], sizeof(palette)); 160 | } 161 | 162 | void frame_present() 163 | { 164 | if ( mode == kFrame1bit ) 165 | convertTo32Bit_1bit(framebuffer1bit, framebuffer32bit); 166 | else 167 | convertTo32Bit_4bit_2x2(framebuffer1bit, framebuffer32bit); 168 | 169 | SDL_UpdateTexture(sdl_texture, NULL, framebuffer32bit, render_w * 4); 170 | 171 | int rw, rh; 172 | SDL_GetRendererOutputSize(renderer, &rw, &rh); 173 | 174 | SDL_Rect src_rect = { 0, 0, render_w, render_h }; 175 | // SDL_Rect dst_rect = { 0, 0, rw, rh }; 176 | SDL_Rect dst_rect = { 40, 0, 1200, 720 }; // XXX don't hardcode 177 | 178 | SDL_RenderCopy(renderer, sdl_texture, &src_rect, &dst_rect); 179 | SDL_RenderPresent(renderer); 180 | } 181 | 182 | #include "pdimage.h" 183 | 184 | const uint8_t dot[5] = { 0x00, 0x00, 0x00, 0x00, 0x02 }; 185 | 186 | const uint8_t digits[10][5] = 187 | { 188 | { 0x07, 0x05, 0x05, 0x05, 0x07 }, // 0 189 | { 0x06, 0x02, 0x02, 0x02, 0x07 }, // 1 190 | { 0x07, 0x01, 0x07, 0x04, 0x07 }, // 2 191 | { 0x07, 0x01, 0x07, 0x01, 0x07 }, // 3 192 | { 0x05, 0x05, 0x07, 0x01, 0x01 }, // 4 193 | { 0x07, 0x04, 0x07, 0x01, 0x07 }, // 5 194 | { 0x07, 0x04, 0x07, 0x05, 0x07 }, // 6 195 | { 0x07, 0x01, 0x01, 0x01, 0x01 }, // 7 196 | { 0x07, 0x05, 0x07, 0x05, 0x07 }, // 8 197 | { 0x07, 0x05, 0x07, 0x01, 0x07 }, // 9 198 | }; 199 | 200 | #include 201 | 202 | void frame_showWaitScreen(const uint8_t* buf) 203 | { 204 | LOG("showWaitScreen()\n"); 205 | memcpy(framebuffer1bit, image_dat, image_dat_len); 206 | 207 | // XXX fix in source data instead 208 | for ( int i = 0; i < LCD_ROWS * LCD_COLUMNS/8; ++i ) 209 | framebuffer1bit[i] ^= 0xff; 210 | 211 | uint8_t* start = framebuffer1bit + (LCD_ROWS - 4) * LCD_COLUMNS/8 - (strlen((char*)buf)+1)/2; 212 | int lohi = 1; 213 | 214 | for ( int x = 0; x < INET_ADDRSTRLEN && buf[x] != 0; ++x ) 215 | { 216 | uint8_t* p = start + x/2; 217 | const uint8_t* dat = (buf[x] == '.') ? dot : digits[buf[x]-'0']; 218 | 219 | for ( int y = 0; y < 5; ++y ) 220 | { 221 | *p |= *dat << (lohi * 4); 222 | p += LCD_COLUMNS/8; 223 | dat += 1; 224 | } 225 | 226 | lohi ^= 1; 227 | } 228 | 229 | LOG("calling frame_present()\n"); 230 | frame_present(); 231 | } 232 | 233 | void frame_begin(uint32_t timestamp_ms) 234 | { 235 | // we should delay to match the timestamp, but.. meh. 236 | } 237 | 238 | void frame_end() 239 | { 240 | // post current frame? 241 | frame_present(); 242 | } 243 | 244 | void frame_setRow(unsigned int rowNum, const uint8_t* row) 245 | { 246 | //LOG("row %i\n", rowNum); 247 | memcpy(framebuffer1bit + (rowNum-1)*LCD_ROWSIZE, row, LCD_ROWSIZE); 248 | } 249 | 250 | -------------------------------------------------------------------------------- /mirrorpi/serial.c: -------------------------------------------------------------------------------- 1 | // 2 | // serial.c 3 | // MirrorJr 4 | // 5 | // Created by Dave Hayden on 9/28/24. 6 | // 7 | 8 | #include "serial.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #if TARGET_RPI 17 | #include 18 | #endif 19 | 20 | #if TARGET_MACOS 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | //#include 29 | 30 | #define kVendorID 0x1331 // Panic! 31 | #define kPlaydateID 0x5740 // Playdate 32 | #endif 33 | 34 | //#include 35 | 36 | //#define LOG printf 37 | #define LOG(...) 38 | 39 | static int g_fd = -1; 40 | //static const char* g_disk_label = NULL; 41 | 42 | #if TARGET_RPI 43 | static char* FindPlaydateSerialPort() 44 | { 45 | struct udev* udev = udev_new(); 46 | 47 | if ( !udev ) 48 | { 49 | LOG("udev_new() failed\n"); 50 | return NULL; 51 | } 52 | 53 | struct udev_enumerate* enumerate = udev_enumerate_new(udev); 54 | 55 | udev_enumerate_add_match_subsystem(enumerate, "tty"); 56 | udev_enumerate_scan_devices(enumerate); 57 | 58 | struct udev_list_entry* list = udev_enumerate_get_list_entry(enumerate); 59 | struct udev_list_entry* node; 60 | char* result = NULL; 61 | 62 | udev_list_entry_foreach(node, list) 63 | { 64 | const char* path = udev_list_entry_get_name(node); 65 | 66 | if ( strncmp(path, "/sys/devices/platform", strlen("/sys/devices/platform")) != 0 ) 67 | continue; 68 | 69 | if ( strncmp(path+strlen(path)-5, "ttyS0", 5) == 0 ) 70 | continue; 71 | 72 | LOG("checking path %s\n", path); 73 | 74 | struct udev_device* dev = udev_device_new_from_syspath(udev, path); 75 | 76 | const char* vendor_id = udev_device_get_property_value(dev, "ID_USB_VENDOR_ID"); 77 | const char* model_id = udev_device_get_property_value(dev, "ID_USB_MODEL_ID"); 78 | const char* devname = udev_device_get_property_value(dev, "DEVNAME"); 79 | 80 | /* 81 | struct udev_list_entry *properties; 82 | properties = udev_device_get_properties_list_entry(dev); 83 | struct udev_list_entry *property; 84 | udev_list_entry_foreach(property, properties) { 85 | printf("Property: %s = %s\n", 86 | udev_list_entry_get_name(property), 87 | udev_list_entry_get_value(property)); 88 | } 89 | */ 90 | 91 | if ( vendor_id != NULL && model_id != NULL && strcmp(vendor_id, "1331") == 0 && strcmp(model_id, "5740") == 0 ) 92 | { 93 | LOG("Found Playdate Device (%s): %s\n", devname, udev_device_get_property_value(dev, "ID_SERIAL_SHORT")); 94 | result = strdup(devname); 95 | udev_device_unref(dev); 96 | break; 97 | } 98 | 99 | udev_device_unref(dev); 100 | } 101 | 102 | udev_enumerate_unref(enumerate); 103 | udev_unref(udev); 104 | 105 | return result; 106 | } 107 | #endif 108 | 109 | #if TARGET_MACOS 110 | bool CreateIOIteratorWithProductID(int product, io_iterator_t *iterator) 111 | { 112 | CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBHostDeviceClassName); 113 | if ( matchingDict == NULL ) 114 | { 115 | fprintf(stderr, "IOServiceMatching returned NULL.\n"); 116 | return false; 117 | } 118 | 119 | int usbVendor = kVendorID; 120 | CFNumberRef refVendorId = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usbVendor); 121 | CFDictionarySetValue (matchingDict, CFSTR (kUSBVendorID), refVendorId); 122 | CFRelease(refVendorId); 123 | 124 | int usbProduct = product; 125 | CFNumberRef refProductId = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &usbProduct); 126 | CFDictionarySetValue (matchingDict, CFSTR (kUSBProductID), refProductId); 127 | CFRelease(refProductId); 128 | 129 | io_iterator_t iter; 130 | kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter); 131 | 132 | if ( kr == KERN_SUCCESS && iterator != NULL ) 133 | *iterator = iter; 134 | 135 | return (kr == KERN_SUCCESS); 136 | } 137 | 138 | static char* FindPlaydateSerialPort() 139 | { 140 | io_iterator_t iter = 0; 141 | 142 | if ( !CreateIOIteratorWithProductID(kPlaydateID, &iter) ) 143 | return NULL; 144 | 145 | char* path = NULL; 146 | CFStringRef portPath = NULL; 147 | io_service_t device; 148 | 149 | while ( (device = IOIteratorNext(iter)) != 0 ) 150 | { 151 | io_name_t deviceName; 152 | 153 | if ( IORegistryEntryGetName(device, deviceName) == KERN_SUCCESS && strcmp(deviceName, "Playdate") == 0 ) 154 | { 155 | portPath = (CFStringRef)IORegistryEntrySearchCFProperty(device, 156 | kIOServicePlane, 157 | CFSTR (kIOCalloutDeviceKey), 158 | kCFAllocatorDefault, 159 | kIORegistryIterateRecursively); 160 | 161 | break; 162 | } 163 | } 164 | 165 | IOObjectRelease(iter); 166 | 167 | if ( portPath != NULL ) 168 | { 169 | path = malloc(PATH_MAX); 170 | CFStringGetCString(portPath, path, PATH_MAX, kCFStringEncodingUTF8); 171 | } 172 | 173 | return path; 174 | } 175 | #endif 176 | 177 | bool ser_open() 178 | { 179 | char* dev = FindPlaydateSerialPort(); 180 | 181 | if ( dev == NULL ) 182 | return false; 183 | 184 | LOG("PlaydateSerialOpen (%s)\n", dev); 185 | 186 | struct flock lock, ourlock; 187 | 188 | if ( g_fd != -1 ) 189 | { 190 | LOG("Serial port was already open (%d)\n", g_fd); 191 | free(dev); 192 | return true; 193 | } 194 | 195 | if ( access(dev, F_OK) == -1 ) 196 | { 197 | LOG("Device %s does not exist\n", dev); 198 | free(dev); 199 | return false; 200 | } 201 | 202 | g_fd = open(dev, O_RDWR | /*O_NONBLOCK |*/ O_NOCTTY | O_SYNC | O_CLOEXEC ); 203 | 204 | if ( g_fd == -1 ) 205 | { 206 | LOG("Couldn't open %s (%d)\n", dev, errno); 207 | free(dev); 208 | return false; 209 | } 210 | 211 | free(dev); 212 | 213 | //lock port 214 | lock.l_type = F_WRLCK; 215 | lock.l_start = 0; 216 | lock.l_whence = SEEK_SET; 217 | lock.l_len = 0; 218 | ourlock = lock; 219 | 220 | fcntl(g_fd, F_GETLK, &lock); 221 | 222 | if ( lock.l_type == F_WRLCK || lock.l_type == F_RDLCK ) 223 | { 224 | LOG("Serial port is in use.\n"); 225 | ser_close(); 226 | return false; 227 | } 228 | else 229 | fcntl(g_fd, F_SETLK, &ourlock); 230 | 231 | struct termios tty; 232 | memset(&tty, 0, sizeof tty); 233 | 234 | if ( tcgetattr(g_fd, &tty) == 0 ) 235 | { 236 | speed_t speed = 115200; 237 | 238 | cfsetospeed(&tty, speed); 239 | cfsetispeed(&tty, speed); 240 | 241 | tty.c_cflag = (CLOCAL | CREAD); /* ignore modem controls */ 242 | tty.c_cflag &= (unsigned)~CSIZE; 243 | tty.c_cflag |= CS8; /* 8-bit characters */ 244 | tty.c_cflag &= (unsigned)~PARENB; /* no parity bit */ 245 | tty.c_cflag &= (unsigned)~CSTOPB; /* only need 1 stop bit */ 246 | tty.c_cflag &= (unsigned)~CRTSCTS; /* no hardware flowcontrol */ 247 | 248 | /* setup for non-canonical mode */ 249 | 250 | tty.c_iflag &= (unsigned)~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); 251 | // tty.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl 252 | tty.c_lflag &= (unsigned)~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); 253 | // tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw 254 | tty.c_oflag &= (unsigned)~OPOST; 255 | 256 | /* fetch bytes as they become available */ 257 | 258 | // MIN == 0; TIME > 0: TIME specifies the limit for a timer in tenths of a second. 259 | // The timer is started when read(2) is called. 260 | // read(2) returns either when at least one byte of data is available, or when the timer expires. 261 | // If the timer expires without any input becoming available, read(2) returns 0. 262 | 263 | tty.c_cc[VMIN] = 0; 264 | tty.c_cc[VTIME] = 1; 265 | 266 | if ( tcsetattr(g_fd, TCSANOW, &tty) != 0 ) 267 | LOG("tcsetattr failed (%d)\n", errno); 268 | } 269 | else 270 | LOG("tcgetattr failed (%d)\n", errno); 271 | 272 | return g_fd != -1; 273 | } 274 | 275 | bool ser_isOpen() 276 | { 277 | return g_fd != -1; 278 | } 279 | 280 | void ser_close() 281 | { 282 | #if DEBUG 283 | LOG("PlaydateSerialClose (%d)", g_fd); 284 | #endif 285 | if (g_fd != -1) 286 | { 287 | struct flock unlock; 288 | unlock.l_type = F_UNLCK; 289 | unlock.l_start = 0; 290 | unlock.l_whence = SEEK_SET; 291 | unlock.l_len = 0; 292 | fcntl(g_fd, F_SETLK, &unlock); 293 | 294 | close(g_fd); 295 | g_fd = -1; 296 | } 297 | else 298 | printf("Serial port was not open.\n"); 299 | } 300 | 301 | static char ser_line_buffer[1024]; 302 | ssize_t ser_writeLine(const char* cmd) 303 | { 304 | //LOG("PlaydateSerialWriteLine (%s)", wxString(cmd)); 305 | 306 | ssize_t len_written = 0; 307 | int cmd_len = snprintf(ser_line_buffer, sizeof(ser_line_buffer), "%s\n", cmd); 308 | 309 | if ( cmd_len > 0 && ser_isOpen() ) 310 | len_written = ser_write(ser_line_buffer, (unsigned)cmd_len); 311 | 312 | return len_written; 313 | } 314 | 315 | ssize_t ser_write(const char* buffer, size_t size) 316 | { 317 | /* 318 | size_t loglen = size; 319 | 320 | while ( loglen > 0 && (buffer[loglen-1] == '\n' || buffer[loglen-1] == '\r') ) 321 | --loglen; 322 | 323 | LOG("ser_write \"%.*s\"\n", loglen, buffer); 324 | */ 325 | 326 | ssize_t len_written = 0; 327 | 328 | if ( size > 0 ) 329 | { 330 | const char* bufptr = buffer; 331 | 332 | while ( ser_isOpen() && size > 0 && len_written >= 0 ) 333 | { 334 | len_written = write(g_fd, bufptr, size); 335 | 336 | if ( len_written > 0 ) 337 | { 338 | size -= (size_t)len_written; 339 | bufptr += len_written; 340 | } 341 | else if ( len_written < 0 && errno == EAGAIN ) 342 | { 343 | //try again 344 | len_written = 0; 345 | } 346 | } 347 | 348 | if ( size != 0 ) 349 | LOG("Serial write Failed: (%zd != %zu)\n", len_written, size); 350 | } 351 | else 352 | LOG("Serial write invalid state.\n"); 353 | 354 | return len_written; 355 | } 356 | 357 | 358 | ssize_t ser_writeNonblocking(const char* buffer, size_t size) 359 | { 360 | /* 361 | size_t loglen = size; 362 | 363 | while ( loglen > 0 && (buffer[loglen-1] == '\n' || buffer[loglen-1] == '\r') ) 364 | --loglen; 365 | 366 | LOG("ser_writeNonblocking \"%.*s\"\n", loglen, buffer); 367 | */ 368 | 369 | //size_t len_written = 0; 370 | 371 | if ( size > 0 && ser_isOpen() ) 372 | { 373 | ssize_t n = write(g_fd, buffer, size); 374 | 375 | if ( n == -1 ) 376 | ser_close(); 377 | else 378 | return n; 379 | } 380 | 381 | return -1; 382 | } 383 | 384 | ssize_t ser_read(uint8_t* buffer, size_t size) 385 | { 386 | if ( !ser_isOpen() ) 387 | return -1; 388 | 389 | ssize_t n = read(g_fd, buffer, size); 390 | 391 | if ( n == -1 ) 392 | { 393 | if ( errno == EAGAIN ) 394 | return 0; 395 | else 396 | ser_close(); 397 | } 398 | 399 | return n; 400 | } 401 | 402 | void ser_flush(void) 403 | { 404 | tcflush(g_fd, TCIOFLUSH); 405 | } 406 | -------------------------------------------------------------------------------- /colorknot/mini3d/render.c: -------------------------------------------------------------------------------- 1 | // 2 | // render.c 3 | // Extension 4 | // 5 | // Created by Dave Hayden on 10/20/15. 6 | // Copyright © 2015 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include "render.h" 12 | 13 | #define LCD_ROWS 120 14 | #define LCD_COLUMNS 200 15 | 16 | #if !defined(MIN) 17 | #define MIN(a, b) (((a)<(b))?(a):(b)) 18 | #endif 19 | 20 | #if !defined(MAX) 21 | #define MAX(a, b) (((a)>(b))?(a):(b)) 22 | #endif 23 | 24 | #if ENABLE_Z_BUFFER 25 | static uint16_t zbuf[LCD_COLUMNS*LCD_ROWS]; 26 | static float zscale; 27 | 28 | #define Z_BIAS 0 29 | 30 | void resetZBuffer(float zmin) 31 | { 32 | memset(zbuf, 0, sizeof(zbuf)); 33 | zscale = 0xffff * (zmin + Z_BIAS); 34 | } 35 | #endif 36 | 37 | static inline uint32_t swap(uint32_t n) 38 | { 39 | #if TARGET_PLAYDATE 40 | //return __REV(n); 41 | uint32_t result; 42 | 43 | __asm volatile ("rev %0, %1" : "=l" (result) : "l" (n)); 44 | return(result); 45 | #else 46 | return ((n & 0xff000000) >> 24) | ((n & 0xff0000) >> 8) | ((n & 0xff00) << 8) | (n << 24); 47 | #endif 48 | } 49 | 50 | /* 51 | #if ENABLE_Z_BUFFER 52 | void getZMask(uint8_t* buffer, int rowstride) 53 | { 54 | for ( int y = 0; y < LCD_ROWS; ++y ) 55 | { 56 | uint32_t* row = (uint32_t*)&buffer[y*rowstride]; 57 | uint16_t* zrow = &zbuf[y*LCD_COLUMNS]; 58 | uint32_t mask = 0; 59 | int x = 0; 60 | 61 | while ( x < rowstride ) 62 | { 63 | if ( zrow[x] < 0xffff ) 64 | mask |= 0x80000000 >> (x%32); 65 | 66 | ++x; 67 | 68 | if ( x%32 == 0 ) 69 | { 70 | *row++ = swap(mask); 71 | mask = 0; 72 | } 73 | } 74 | 75 | if ( x%32 != 0 ) 76 | *row++ = swap(mask); 77 | } 78 | } 79 | #endif 80 | */ 81 | 82 | static void 83 | drawFragment(uint8_t* row, int x1, int x2, uint8_t color) 84 | { 85 | memset(row+x1, color, x2-x1); 86 | } 87 | 88 | #if ENABLE_Z_BUFFER 89 | static void 90 | drawFragment_zbuf(uint8_t* row, uint16_t* zbrow, int x, int endx, uint32_t z, int32_t dzdx, uint8_t color) 91 | { 92 | if ( endx < 0 || x >= LCD_COLUMNS ) 93 | return; 94 | 95 | if ( x < 0 ) 96 | { 97 | z += -x * dzdx; 98 | x = 0; 99 | } 100 | 101 | if ( endx > LCD_COLUMNS ) 102 | endx = LCD_COLUMNS; 103 | 104 | while ( x < endx ) 105 | { 106 | uint16_t zi = z >> 16; 107 | 108 | if ( zi > zbrow[x] ) 109 | { 110 | row[x] = color; 111 | zbrow[x] = zi; 112 | } 113 | 114 | ++x; 115 | z += dzdx; 116 | } 117 | } 118 | #endif 119 | 120 | static inline int32_t slope(float x1, float y1, float x2, float y2) 121 | { 122 | float dx = x2-x1; 123 | float dy = y2-y1; 124 | 125 | if ( dy < 1 ) 126 | return dx * (1<<16); 127 | else 128 | return dx / dy * (1<<16); 129 | } 130 | 131 | #if ENABLE_Z_BUFFER 132 | LCDRowRange 133 | drawLine_zbuf(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, int thick, uint8_t color) 134 | { 135 | if ( p1->y > p2->y ) 136 | { 137 | Point3D* tmp = p1; 138 | p1 = p2; 139 | p2 = tmp; 140 | } 141 | 142 | int y = p1->y; 143 | int endy = p2->y; 144 | 145 | if ( y >= LCD_ROWS || endy < 0 || MIN(p1->x, p2->x) >= LCD_COLUMNS || MAX(p1->x, p2->x) < 0 ) 146 | return (LCDRowRange){ 0, 0 }; 147 | 148 | int32_t x = p1->x * (1<<16); 149 | int32_t dx = slope(p1->x, p1->y, p2->x, p2->y + 1); 150 | float py = p1->y; 151 | 152 | // move lines a bit forward so they don't get buried in solid geometry 153 | float z1 = zscale / (p1->z + Z_BIAS) + 256; 154 | float z2 = zscale / (p2->z + Z_BIAS) + 256; 155 | 156 | if ( z1 > 65535 ) z1 = 65535; 157 | if ( z2 > 65535 ) z2 = 65535; 158 | 159 | int32_t dzdy = slope(z1, p1->y, z2, p2->y + 1); 160 | int32_t dzdx = slope(z1, p1->x, z2, p2->x); 161 | 162 | uint32_t z = z1 * (1<<16); 163 | 164 | if ( y < 0 ) 165 | { 166 | x += -y * dx; 167 | z += -y * dzdy; 168 | y = 0; 169 | py = 0; 170 | } 171 | 172 | int32_t x1 = x + dx * (y+1-py); 173 | 174 | while ( y <= endy ) 175 | { 176 | if ( y == endy ) 177 | x1 = p2->x * (1<<16); 178 | 179 | if ( dx < 0 ) 180 | { 181 | z += dzdy; 182 | drawFragment_zbuf(&bitmap[y*rowstride], &zbuf[y*LCD_COLUMNS], x1>>16, (x>>16) + thick, z, dzdx, color); 183 | } 184 | else 185 | { 186 | drawFragment_zbuf(&bitmap[y*rowstride], &zbuf[y*LCD_COLUMNS], x>>16, (x1>>16) + thick, z, dzdx, color); 187 | z += dzdy; 188 | } 189 | 190 | if ( ++y == LCD_ROWS ) 191 | break; 192 | 193 | x = x1; 194 | x1 += dx; 195 | } 196 | 197 | return (LCDRowRange){ MAX(0, p1->y), MIN(LCD_ROWS, p2->y) }; 198 | } 199 | #endif 200 | 201 | LCDRowRange 202 | drawLine(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, int thick, uint8_t color) 203 | { 204 | if ( p1->y > p2->y ) 205 | { 206 | Point3D* tmp = p1; 207 | p1 = p2; 208 | p2 = tmp; 209 | } 210 | 211 | int y = p1->y; 212 | int endy = p2->y; 213 | 214 | if ( y >= LCD_ROWS || endy < 0 || MIN(p1->x, p2->x) >= LCD_COLUMNS || MAX(p1->x, p2->x) < 0 ) 215 | return (LCDRowRange){ 0, 0 }; 216 | 217 | int32_t x = p1->x * (1<<16); 218 | int32_t dx = slope(p1->x, p1->y, p2->x, p2->y); 219 | float py = p1->y; 220 | 221 | if ( y < 0 ) 222 | { 223 | x += -p1->y * dx; 224 | y = 0; 225 | py = 0; 226 | } 227 | 228 | int32_t x1 = x + dx * (y+1-py); 229 | 230 | while ( y <= endy ) 231 | { 232 | if ( y == endy ) 233 | x1 = p2->x * (1<<16); 234 | 235 | if ( dx < 0 ) 236 | drawFragment(&bitmap[y*rowstride], x1>>16, (x>>16) + thick, color); 237 | else 238 | drawFragment(&bitmap[y*rowstride], x>>16, (x1>>16) + thick, color); 239 | 240 | if ( ++y == LCD_ROWS ) 241 | break; 242 | 243 | x = x1; 244 | x1 += dx; 245 | } 246 | 247 | return (LCDRowRange){ MAX(0, p1->y), MIN(LCD_ROWS, p2->y) }; 248 | } 249 | 250 | static void fillRange(uint8_t* bitmap, int rowstride, int y, int endy, int32_t* x1p, int32_t dx1, int32_t* x2p, int32_t dx2, uint8_t color) 251 | { 252 | int32_t x1 = *x1p, x2 = *x2p; 253 | 254 | if ( endy < 0 ) 255 | { 256 | int dy = endy - y; 257 | *x1p = x1 + dy * dx1; 258 | *x2p = x2 + dy * dx2; 259 | return; 260 | } 261 | 262 | if ( y < 0 ) 263 | { 264 | x1 += -y * dx1; 265 | x2 += -y * dx2; 266 | y = 0; 267 | } 268 | 269 | while ( y < endy ) 270 | { 271 | drawFragment(&bitmap[y*rowstride], (x1>>16), (x2>>16)+1, color); 272 | 273 | x1 += dx1; 274 | x2 += dx2; 275 | ++y; 276 | } 277 | 278 | *x1p = x1; 279 | *x2p = x2; 280 | } 281 | 282 | #if ENABLE_Z_BUFFER 283 | static void fillRange_zbuf(uint8_t* bitmap, int rowstride, int y, int endy, int32_t* x1p, int32_t dx1, int32_t* x2p, int32_t dx2, uint32_t* zp, int32_t dzdy, int32_t dzdx, uint8_t color) 284 | { 285 | int32_t x1 = *x1p, x2 = *x2p; 286 | uint32_t z = *zp; 287 | 288 | if ( endy < 0 ) 289 | { 290 | int dy = endy - y; 291 | *x1p = x1 + dy * dx1; 292 | *x2p = x2 + dy * dx2; 293 | *zp = z + dy * dzdy; 294 | return; 295 | } 296 | 297 | if ( y < 0 ) 298 | { 299 | x1 += -y * dx1; 300 | x2 += -y * dx2; 301 | z += -y * dzdy; 302 | y = 0; 303 | } 304 | 305 | while ( y < endy ) 306 | { 307 | drawFragment_zbuf(&bitmap[y*rowstride], &zbuf[y*LCD_COLUMNS], (x1>>16), (x2>>16)+1, z, dzdx, color); 308 | 309 | x1 += dx1; 310 | x2 += dx2; 311 | z += dzdy; 312 | ++y; 313 | } 314 | 315 | *x1p = x1; 316 | *x2p = x2; 317 | *zp = z; 318 | } 319 | #endif 320 | 321 | static inline void sortTri(Point3D** p1, Point3D** p2, Point3D** p3) 322 | { 323 | float y1 = (*p1)->y, y2 = (*p2)->y, y3 = (*p3)->y; 324 | 325 | if ( y1 <= y2 && y1 < y3 ) 326 | { 327 | if ( y3 < y2 ) // 1,3,2 328 | { 329 | Point3D* tmp = *p2; 330 | *p2 = *p3; 331 | *p3 = tmp; 332 | } 333 | } 334 | else if ( y2 < y1 && y2 < y3 ) 335 | { 336 | Point3D* tmp = *p1; 337 | *p1 = *p2; 338 | 339 | if ( y3 < y1 ) // 2,3,1 340 | { 341 | *p2 = *p3; 342 | *p3 = tmp; 343 | } 344 | else // 2,1,3 345 | *p2 = tmp; 346 | } 347 | else 348 | { 349 | Point3D* tmp = *p1; 350 | *p1 = *p3; 351 | 352 | if ( y1 < y2 ) // 3,1,2 353 | { 354 | *p3 = *p2; 355 | *p2 = tmp; 356 | } 357 | else // 3,2,1 358 | *p3 = tmp; 359 | } 360 | 361 | } 362 | 363 | LCDRowRange fillTriangle(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, Point3D* p3, uint8_t color) 364 | { 365 | // sort by y coord 366 | 367 | sortTri(&p1, &p2, &p3); 368 | 369 | int endy = MIN(LCD_ROWS, p3->y); 370 | 371 | if ( p1->y > LCD_ROWS || endy < 0 ) 372 | return (LCDRowRange){ 0, 0 }; 373 | 374 | int32_t x1 = p1->x * (1<<16); 375 | int32_t x2 = x1; 376 | 377 | int32_t sb = slope(p1->x, p1->y, p2->x, p2->y); 378 | int32_t sc = slope(p1->x, p1->y, p3->x, p3->y); 379 | 380 | int32_t dx1 = MIN(sb, sc); 381 | int32_t dx2 = MAX(sb, sc); 382 | 383 | fillRange(bitmap, rowstride, p1->y, MIN(LCD_ROWS, p2->y), &x1, dx1, &x2, dx2, color); 384 | 385 | int dx = slope(p2->x, p2->y, p3->x, p3->y); 386 | 387 | if ( sb < sc ) 388 | { 389 | x1 = p2->x * (1<<16); 390 | fillRange(bitmap, rowstride, p2->y, endy, &x1, dx, &x2, dx2, color); 391 | } 392 | else 393 | { 394 | x2 = p2->x * (1<<16); 395 | fillRange(bitmap, rowstride, p2->y, endy, &x1, dx1, &x2, dx, color); 396 | } 397 | 398 | return (LCDRowRange){ MAX(0, p1->y), endy }; 399 | } 400 | 401 | #if ENABLE_Z_BUFFER 402 | LCDRowRange fillTriangle_zbuf(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, Point3D* p3, uint8_t color) 403 | { 404 | sortTri(&p1, &p2, &p3); 405 | 406 | int endy = MIN(LCD_ROWS, p3->y); 407 | int det = (p3->x - p1->x) * (p2->y - p1->y) - (p2->x - p1->x) * (p3->y - p1->y); 408 | 409 | if ( p1->y > LCD_ROWS || endy < 0 || det == 0 ) 410 | return (LCDRowRange){ 0, 0 }; 411 | 412 | int32_t x1 = p1->x * (1<<16); 413 | int32_t x2 = x1; 414 | 415 | int32_t sb = slope(p1->x, p1->y, p2->x, p2->y); 416 | int32_t sc = slope(p1->x, p1->y, p3->x, p3->y); 417 | 418 | int32_t dx1 = MIN(sb,sc); 419 | int32_t dx2 = MAX(sb,sc); 420 | 421 | float z1 = zscale / (p1->z + Z_BIAS); 422 | float z2 = zscale / (p2->z + Z_BIAS); 423 | float z3 = zscale / (p3->z + Z_BIAS); 424 | 425 | float mx = p1->x + (p2->y-p1->y) * (p3->x-p1->x) / (p3->y-p1->y); 426 | float mz = z1 + (p2->y-p1->y) * (z3-z1) / (p3->y-p1->y); 427 | 428 | int32_t dzdx, dzdy; 429 | 430 | if ( sc < sb ) 431 | { 432 | dzdx = slope(mz, mx, z2, p2->x); 433 | dzdy = slope(z1, p1->y, z3, p3->y); 434 | } 435 | else 436 | { 437 | dzdx = slope(z2, p2->x, mz, mx); 438 | dzdy = slope(z1, p1->y, z2, p2->y); 439 | } 440 | 441 | uint32_t z = z1 * (1<<16); 442 | 443 | fillRange_zbuf(bitmap, rowstride, p1->y, MIN(LCD_ROWS, p2->y), &x1, dx1, &x2, dx2, &z, dzdy, dzdx, color); 444 | 445 | int dx = slope(p2->x, p2->y, p3->x, p3->y); 446 | 447 | if ( sb < sc ) 448 | { 449 | dzdy = slope(z2, p2->y, z3, p3->y); 450 | x1 = p2->x * (1<<16); 451 | z = z2 * (1<<16); 452 | fillRange_zbuf(bitmap, rowstride, p2->y, endy, &x1, dx, &x2, dx2, &z, dzdy, dzdx, color); 453 | } 454 | else 455 | { 456 | x2 = p2->x * (1<<16); 457 | fillRange_zbuf(bitmap, rowstride, p2->y, endy, &x1, dx1, &x2, dx, &z, dzdy, dzdx, color); 458 | } 459 | 460 | return (LCDRowRange){ MAX(0, p1->y), endy }; 461 | } 462 | #endif 463 | 464 | LCDRowRange fillQuad(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, Point3D* p3, Point3D* p4, uint8_t color) 465 | { 466 | // XXX - implement with 3 fillRange_zbuf() calls 467 | fillTriangle(bitmap, rowstride, p1, p2, p3, color); 468 | return fillTriangle(bitmap, rowstride, p1, p3, p4, color); 469 | } 470 | 471 | #if ENABLE_Z_BUFFER 472 | LCDRowRange fillQuad_zbuf(uint8_t* bitmap, int rowstride, Point3D* p1, Point3D* p2, Point3D* p3, Point3D* p4, uint8_t color) 473 | { 474 | // XXX - implement with 3 fillRange_zbuf() calls 475 | fillTriangle_zbuf(bitmap, rowstride, p1, p2, p3, color); 476 | return fillTriangle_zbuf(bitmap, rowstride, p1, p3, p4, color); 477 | } 478 | #endif 479 | -------------------------------------------------------------------------------- /mirrorpi/stream.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include "stream.h" 5 | #include "serial.h" 6 | #include "frame.h" 7 | #include "audio.h" 8 | #include "ringbuffer.h" 9 | 10 | #define LOG printf 11 | //#define LOG(...) 12 | 13 | //static const char ECHO_ON[] = "echo on\r\n"; 14 | //static const char STREAM[] = "stream\r\n"; 15 | static const char STREAM_ENABLE[] = "stream enable\r\n"; 16 | static const char STREAM_FULLFRAME[] = "stream fullframe\r\n"; 17 | static const char STREAM_DISABLE[] = "stream disable\r\n"; 18 | static const char STREAM_POKE[] = "stream poke\r\n"; 19 | 20 | enum StreamProtocolState { 21 | kStreamDisabled, 22 | kStreamEnabling, 23 | kStreamStreamStarting, 24 | kStreamParsingFirstHeader, 25 | kStreamParsingHeader, 26 | kStreamParsingPayload, 27 | kStreamParsingPoke 28 | } state; 29 | 30 | #define MAX_PAYLOAD_SIZE (sizeof(MessageFrameData) + FRAME_SIZE_BYTES) 31 | uint8_t payload[MAX_PAYLOAD_SIZE]; 32 | int payloadBytesRead = 0; 33 | 34 | enum StreamAudioConfig audio_config = kAudioStereo16; 35 | enum StreamAudioMute audio_mute = kAudioMuteOff; 36 | 37 | //! String to expect back on enable 38 | int expectedBytesRead = 0; 39 | 40 | // wxTimer enable_timer_{this, TIMER_StreamProtocolEnable}; 41 | // wxTimer start_timer_{this, TIMER_StreamProtocolStart}; 42 | // wxTimer poke_timer_{this, TIMER_StreamProtocolPoke}; 43 | 44 | //void streamEnabled(); 45 | 46 | int64_t stream_start_ms = 0; 47 | RingBuffer serialbuf; 48 | #define BUFFER_SIZE 65536 49 | unsigned int buffer_highwater = 0; 50 | 51 | void handleStreamMessage(MessageHeader *hdr); 52 | 53 | bool stream_init() 54 | { 55 | RingBuffer_init(&serialbuf); 56 | RingBuffer_setSize(&serialbuf, BUFFER_SIZE, 1); 57 | 58 | //usleep(10000); 59 | 60 | // try to flush input 61 | /* 62 | int n; 63 | char buf[256]; 64 | 65 | while ( (n = ser_readNonblocking(buf, sizeof(buf))) > 0 ) 66 | ; 67 | 68 | return n == 0; 69 | */ 70 | return true; 71 | } 72 | 73 | void sendStreamOption(const char* opt) 74 | { 75 | char buf[32]; 76 | snprintf(buf, 32, "stream %s\r\n", opt); 77 | ser_writeNonblocking(buf, strlen(buf)); 78 | } 79 | 80 | void disableStream() 81 | { 82 | if ( state != kStreamDisabled ) 83 | { 84 | //poke_timer.Stop(); 85 | ser_writestr(STREAM_DISABLE); 86 | state = kStreamDisabled; 87 | } 88 | } 89 | 90 | void disconnect() 91 | { 92 | disableStream(); 93 | LOG("Closing serial port\n"); 94 | ser_close(); 95 | } 96 | 97 | void sendStreamEnable() 98 | { 99 | expectedBytesRead = 0; 100 | printf("resetting stream\n"); 101 | state = kStreamEnabling; 102 | //ser_writestr(ECHO_ON); 103 | ser_writestr(STREAM_ENABLE); 104 | 105 | // enable full frame updates and application commands 106 | ser_writestr(STREAM_FULLFRAME); 107 | } 108 | 109 | void reconnect() 110 | { 111 | disableStream(); 112 | LOG("Flushing connection\n"); 113 | usleep(10000); 114 | ser_flush(); 115 | LOG("Re-enabling stream\n"); 116 | sendStreamEnable(); 117 | } 118 | 119 | void sendButtonPress(int button); 120 | void sendButtonRelease(int button); 121 | void sendCrankEnable(bool enable); 122 | void sendCrankChange(double angle_change); 123 | //void sendAccelChange(double x, double y, double z); 124 | 125 | void stream_poke() 126 | { 127 | // let device know we're still here 128 | ser_writeNonblocking(STREAM_POKE, strlen(STREAM_POKE)); 129 | } 130 | 131 | const char* getOptionString(enum StreamAudioConfig cfg) 132 | { 133 | switch ( cfg ) 134 | { 135 | case kAudioDisabled: return "a-"; 136 | case kAudioStereo16: return "a+"; 137 | case kAudioMono16: return "am"; 138 | default: return NULL; 139 | } 140 | } 141 | 142 | void setAudioConfiguration(enum StreamAudioConfig cfg) 143 | { 144 | if ( cfg == audio_config ) 145 | return; 146 | 147 | audio_config = cfg; 148 | 149 | if ( state != kStreamDisabled ) 150 | { 151 | const char* s = getOptionString(cfg); 152 | 153 | if ( s != NULL ) 154 | sendStreamOption(s); 155 | } 156 | } 157 | 158 | void setMuteValue(enum StreamAudioMute value) 159 | { 160 | audio_mute = value; 161 | ser_writestr(value == kAudioMuteOn ? "mute on\r\n" : "mute off\r\n"); 162 | } 163 | 164 | uint32_t swap_bits(uint8_t n) 165 | { 166 | n = ((n & 0x55) << 1) | ((n & 0xaa) >> 1); 167 | n = ((n & 0x33) << 2) | ((n & 0xcc) >> 2); 168 | n = ((n & 0x0f) << 4) | ((n & 0xf0) >> 4); 169 | return n; 170 | } 171 | 172 | bool prv_is_valid_header(MessageHeader hdr) 173 | { 174 | const uint16_t sz = hdr.payload_length; 175 | 176 | switch ( hdr.opcode ) 177 | { 178 | case OPCODE_DEVICE_STATE: 179 | return (sz == sizeof(MessageDeviceState)); 180 | case OPCODE_FRAME_BEGIN_DEPRECATED: 181 | return (sz == 0); 182 | case OPCODE_FRAME_BEGIN: 183 | return (sz == sizeof(MessageFrameBegin)); 184 | case OPCODE_FRAME_END: 185 | return (sz == 0); 186 | case OPCODE_FRAME_ROW: 187 | return (sz == sizeof(MessageRowData)); 188 | case OPCODE_FULL_FRAME: 189 | { 190 | unsigned int rows = hdr.unused; 191 | return (sz == sizeof(MessageFrameData) + rows * 50); 192 | } 193 | case OPCODE_AUDIO_FRAME: 194 | return (sz < 2048); 195 | case OPCODE_AUDIO_CHANGE: 196 | return (sz == sizeof(MessageAudioChange)); 197 | case OPCODE_AUDIO_OFFSET: 198 | return (sz == sizeof(MessageAudioOffset)); 199 | case OPCODE_APPLICATION: 200 | { 201 | if ( hdr.unused == APPLICATION_COMMAND_RESET ) 202 | return (sz == 0); 203 | else if ( hdr.unused == APPLICATION_COMMAND_1BIT_PALETTE ) 204 | return (sz == sizeof(Message1bitPalette)); 205 | else if ( hdr.unused == APPLICATION_COMMAND_4BIT_PALETTE ) 206 | return (sz == sizeof(Message4bitPalette)); 207 | else 208 | return true; 209 | } 210 | case 's': 211 | return (sz == 25970); // "stream poke" 212 | } 213 | return false; 214 | } 215 | 216 | const char* to_string(enum StreamAudioConfig cfg) 217 | { 218 | switch (cfg) 219 | { 220 | case kAudioDisabled: return "disabled"; 221 | case kAudioStereo16: return "stereo16"; 222 | case kAudioMono16: return "mono16"; 223 | default: return "???"; 224 | } 225 | } 226 | 227 | enum StreamAudioConfig toStreamAudioConfig(const char* s) 228 | { 229 | if ( strcmp(s, "stereo16") == 0 ) 230 | return kAudioStereo16; 231 | else if ( strcmp(s, "mono16") == 0 ) 232 | return kAudioMono16; 233 | else 234 | return kAudioDisabled; 235 | } 236 | 237 | enum StreamAudioConfig flagsToStreamAudioConfig(uint8_t flags) 238 | { 239 | if ( (flags & STREAM_AUDIO_FLAG_ENABLED) == 0 ) 240 | return kAudioDisabled; 241 | else if ( flags & STREAM_AUDIO_FLAG_STEREO ) 242 | return kAudioStereo16; 243 | else 244 | return kAudioMono16; 245 | } 246 | 247 | void streamStarted() 248 | { 249 | //stream_start_ms = wxGetUTCTimeMillis().GetValue(); 250 | 251 | const char* opt = getOptionString(audio_config); 252 | 253 | // Try to enable audio if needed & supported by device 254 | if ( opt != NULL ) 255 | sendStreamOption(opt); 256 | 257 | setMuteValue(audio_mute); 258 | } 259 | 260 | static void handleStreamPayload(); 261 | 262 | MessageHeader header; 263 | int headerBytesRead = 0; 264 | int pokeBytesRead = 0; 265 | 266 | #define MAX(a,b) (((a)>(b))?(a):(b)) 267 | 268 | void stream_addData(uint8_t* buf, unsigned int len) 269 | { 270 | for ( ;; ) 271 | { 272 | unsigned int n = RingBuffer_addData(&serialbuf, buf, len); 273 | unsigned int avail = RingBuffer_getBytesAvailable(&serialbuf); 274 | 275 | if ( avail > buffer_highwater ) 276 | { 277 | buffer_highwater = avail; 278 | printf("serial buf max: %i bytes\n", avail); 279 | } 280 | 281 | if ( (len -= n) == 0 ) 282 | break; 283 | 284 | buf += n; 285 | printf("serial ringbuffer full\n"); 286 | usleep(1000); 287 | } 288 | } 289 | 290 | bool stream_processbuf(uint8_t* buf, unsigned int len) 291 | { 292 | uint8_t* end = buf + len; 293 | 294 | while ( buf < end ) 295 | { 296 | switch ( state ) 297 | { 298 | case kStreamDisabled: 299 | return true; 300 | 301 | case kStreamEnabling: 302 | { 303 | while ( buf < end && expectedBytesRead < strlen(STREAM_ENABLE) ) 304 | { 305 | if ( *buf++ == STREAM_ENABLE[expectedBytesRead] ) 306 | ++expectedBytesRead; 307 | else 308 | expectedBytesRead = 0; 309 | } 310 | 311 | if ( expectedBytesRead == strlen(STREAM_ENABLE) ) 312 | { 313 | printf("stream enable received\n"); 314 | //streamEnabled(); 315 | state = kStreamStreamStarting; 316 | } 317 | 318 | break; 319 | } 320 | case kStreamStreamStarting: 321 | { 322 | // XXX - first opcode received is 0x0d = \n :( 323 | 324 | // skip errant \r and \n 325 | //if ( *buf != '\r' && *buf != '\n' ) 326 | //{ 327 | headerBytesRead = 0; 328 | state = kStreamParsingFirstHeader; 329 | //} 330 | //else 331 | // ++buf; 332 | 333 | break; 334 | } 335 | case kStreamParsingFirstHeader: 336 | case kStreamParsingHeader: 337 | { 338 | while ( buf < end && headerBytesRead < sizeof(MessageHeader) ) 339 | ((uint8_t*)&header)[headerBytesRead++] = *buf++; 340 | 341 | if ( headerBytesRead < sizeof(MessageHeader) ) 342 | return true; 343 | 344 | if ( strncmp((char*)&header, "stre", 4) == 0 ) 345 | { 346 | printf("read \"stre\", scanning for \"stream poke\"\n"); 347 | pokeBytesRead = 4; 348 | state = kStreamParsingPoke; 349 | continue; 350 | } 351 | 352 | if ( !prv_is_valid_header(header) ) 353 | { 354 | LOG("Invalid header opcode %d length %d, reconnecting\n", header.opcode, header.payload_length); 355 | // exit(0); 356 | reconnect(); 357 | return false; 358 | } 359 | 360 | //LOG("Received opcode %u sz %u\n", header.opcode, header.payload_length); 361 | 362 | if ( state == kStreamParsingFirstHeader ) 363 | streamStarted(); 364 | 365 | payloadBytesRead = 0; 366 | state = kStreamParsingPayload; 367 | 368 | break; 369 | } 370 | case kStreamParsingPayload: 371 | { 372 | const size_t payload_size = header.payload_length; 373 | 374 | while ( buf < end && payloadBytesRead < payload_size ) 375 | payload[payloadBytesRead++] = *buf++; 376 | 377 | if ( payload_size > 0 && payloadBytesRead < payload_size ) 378 | return true; 379 | 380 | //LOG("Finished parsing message\n"); 381 | handleStreamPayload(); 382 | 383 | headerBytesRead = 0; 384 | state = kStreamParsingHeader; 385 | break; 386 | } 387 | case kStreamParsingPoke: 388 | { 389 | int len = strlen(STREAM_POKE); 390 | 391 | // while ( buf < end && pokeBytesRead < len && *buf == STREAM_POKE[pokeBytesRead] ) 392 | // ++pokeBytesRead; 393 | 394 | while ( buf < end ) 395 | { 396 | if ( pokeBytesRead < len ) 397 | { 398 | printf("looking for char %i %c (%02x), got %c (%02x)\n", pokeBytesRead, STREAM_POKE[pokeBytesRead], STREAM_POKE[pokeBytesRead], *buf, *buf); 399 | 400 | if ( *buf == STREAM_POKE[pokeBytesRead] || *buf == STREAM_ENABLE[pokeBytesRead] ) 401 | { 402 | ++pokeBytesRead; 403 | ++buf; 404 | } 405 | else 406 | break; 407 | } 408 | else 409 | break; 410 | } 411 | 412 | if ( pokeBytesRead == len ) 413 | { 414 | printf("found \"stream poke\", back to payloads\n"); 415 | headerBytesRead = 0; 416 | state = kStreamParsingHeader; 417 | } 418 | else if ( buf < end ) 419 | { 420 | printf("failed reading stream poke\n"); 421 | exit(0); 422 | } 423 | 424 | break; 425 | } 426 | } 427 | } 428 | 429 | return true; 430 | } 431 | 432 | bool stream_process() 433 | { 434 | unsigned int n; 435 | 436 | while ( (n = RingBuffer_getOutputAvailableSize(&serialbuf)) > 0 ) 437 | { 438 | if ( !stream_processbuf(RingBuffer_getOutputPointer(&serialbuf), n) ) 439 | return false; 440 | 441 | RingBuffer_moveOutputPointer(&serialbuf, n); 442 | } 443 | 444 | return true; 445 | } 446 | 447 | static void handleStreamPayload() 448 | { 449 | if ( header.opcode == OPCODE_FRAME_BEGIN_DEPRECATED ) 450 | { 451 | int64_t ts_ms = 0; //wxGetUTCTimeMillis().GetValue() - stream_start_ms; 452 | frame_begin((uint32_t)ts_ms); 453 | } 454 | else if ( header.opcode == OPCODE_FRAME_BEGIN ) 455 | { 456 | MessageFrameBegin* rb = (MessageFrameBegin*)&payload[0]; 457 | frame_begin(rb->timestamp_ms); 458 | } 459 | else if ( header.opcode == OPCODE_FRAME_ROW ) 460 | { 461 | MessageRowData* rd = (MessageRowData*)&payload[0]; 462 | frame_setRow(swap_bits(rd->rowNum), rd->data); 463 | } 464 | else if ( header.opcode == OPCODE_FRAME_END ) 465 | { 466 | frame_end(); 467 | } 468 | else if ( header.opcode == OPCODE_FULL_FRAME ) 469 | { 470 | MessageFrameData* fd = (MessageFrameData*)&payload[0]; 471 | 472 | frame_begin(fd->timestamp_ms); 473 | 474 | for ( unsigned int i = 0, row = 0; i < FRAME_HEIGHT; ++i ) 475 | { 476 | if ( fd->rowmask[i/8] & (1<<(i%8)) ) 477 | frame_setRow(i+1, fd->data + 2 + ROW_SIZE_BYTES * row++); 478 | } 479 | 480 | frame_end(); 481 | } 482 | else if ( header.opcode == OPCODE_AUDIO_CHANGE ) 483 | { 484 | MessageAudioChange* ac = (MessageAudioChange*)&payload[0]; 485 | unsigned int num_channels = (ac->flags & STREAM_AUDIO_FLAG_STEREO) ? 2 : 1; 486 | audio_setFormat(num_channels); 487 | } 488 | else if ( header.opcode == OPCODE_AUDIO_FRAME ) 489 | { 490 | if ( audio_config != kAudioDisabled ) 491 | { 492 | MessageAudioFrame* af = (MessageAudioFrame*)&payload[0]; 493 | audio_addData(af->data, header.payload_length); 494 | } 495 | } 496 | else if ( header.opcode == OPCODE_AUDIO_OFFSET ) 497 | { 498 | if ( audio_config != kAudioDisabled ) 499 | { 500 | MessageAudioOffset* ao = (MessageAudioOffset*)&payload[0]; 501 | audio_addSilence(ao->offset_samples); 502 | } 503 | } 504 | else if ( header.opcode == OPCODE_DEVICE_STATE ) 505 | { 506 | MessageDeviceState* state = (MessageDeviceState*)&payload[0]; 507 | static int lastdropped = -1; 508 | int dropped = state->unused; 509 | if ( dropped != lastdropped && lastdropped != -1 ) 510 | printf("%i messages dropped\n", dropped>lastdropped ? dropped-lastdropped : dropped+65536-lastdropped); 511 | lastdropped = dropped; 512 | } 513 | else if ( header.opcode == OPCODE_APPLICATION ) 514 | { 515 | // mode/palette change message 516 | if ( header.unused == APPLICATION_COMMAND_RESET ) 517 | frame_reset(); 518 | else if ( header.unused == APPLICATION_COMMAND_1BIT_PALETTE ) 519 | frame_set1BitPalette((RGB*)payload); 520 | else if ( header.unused == APPLICATION_COMMAND_4BIT_PALETTE ) 521 | frame_set4BitPalette((RGB*)payload); 522 | } 523 | } 524 | 525 | void stream_begin() 526 | { 527 | ser_flush(); 528 | sendStreamEnable(); 529 | frame_reset(); 530 | //stream_start_ms = wxGetUTCTimeMillis().GetValue(); 531 | } 532 | 533 | void stream_reset() 534 | { 535 | ser_flush(); 536 | expectedBytesRead = 0; 537 | state = kStreamDisabled; 538 | RingBuffer_reset(&serialbuf); 539 | } 540 | 541 | static const char keynames[] = "udlrbam"; 542 | 543 | void stream_sendButtonPress(int btn) 544 | { 545 | char buf[] = "btn +x\r\n"; 546 | buf[5] = keynames[btn]; 547 | printf("sending +%c\n", buf[5]); 548 | ser_writeNonblocking(buf, strlen(buf)); 549 | } 550 | 551 | void stream_sendButtonRelease(int btn) 552 | { 553 | char buf[] = "btn -x\r\n"; 554 | buf[5] = keynames[btn]; 555 | printf("sending -%c\n", buf[5]); 556 | ser_writeNonblocking(buf, strlen(buf)); 557 | } 558 | 559 | void stream_sendCrankChange(float angle_change) 560 | { 561 | char buf[32]; 562 | snprintf(buf, 32, "changecrank %.1f\r\n", angle_change); 563 | ser_writeNonblocking(buf, strlen(buf)); 564 | } 565 | 566 | void stream_sendAccelChange(double x, double y, double z) 567 | { 568 | char buf[32]; 569 | snprintf(buf, 32, "accel %i %i %i\r\n", (int)(x*1000), (int)(y*1000), (int)(z*1000)); 570 | ser_writeNonblocking(buf, strlen(buf)); 571 | } 572 | 573 | void stream_sendCrankDocked(bool docked) 574 | { 575 | const char* msg = docked ? "dockcrank 1\r\n" : "dockcrank 0\r\n"; 576 | ser_writeNonblocking(msg, strlen(msg)); 577 | } 578 | -------------------------------------------------------------------------------- /crank/sensor1/sensor.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.0, 41 | "height": 1.143, 42 | "width": 1.143 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 0.762, 47 | "silk_text_size_v": 0.762, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.5 52 | } 53 | }, 54 | "diff_pair_dimensions": [ 55 | { 56 | "gap": 0.0, 57 | "via_gap": 0.0, 58 | "width": 0.0 59 | } 60 | ], 61 | "drc_exclusions": [], 62 | "meta": { 63 | "version": 2 64 | }, 65 | "rule_severities": { 66 | "annular_width": "error", 67 | "clearance": "error", 68 | "connection_width": "warning", 69 | "copper_edge_clearance": "error", 70 | "copper_sliver": "warning", 71 | "courtyards_overlap": "error", 72 | "creepage": "error", 73 | "diff_pair_gap_out_of_range": "error", 74 | "diff_pair_uncoupled_length_too_long": "error", 75 | "drill_out_of_range": "error", 76 | "duplicate_footprints": "warning", 77 | "extra_footprint": "warning", 78 | "footprint": "error", 79 | "footprint_filters_mismatch": "ignore", 80 | "footprint_symbol_mismatch": "warning", 81 | "footprint_type_mismatch": "ignore", 82 | "hole_clearance": "error", 83 | "hole_near_hole": "error", 84 | "hole_to_hole": "error", 85 | "holes_co_located": "warning", 86 | "invalid_outline": "error", 87 | "isolated_copper": "warning", 88 | "item_on_disabled_layer": "error", 89 | "items_not_allowed": "error", 90 | "length_out_of_range": "error", 91 | "lib_footprint_issues": "warning", 92 | "lib_footprint_mismatch": "warning", 93 | "malformed_courtyard": "error", 94 | "microvia_drill_out_of_range": "error", 95 | "mirrored_text_on_front_layer": "warning", 96 | "missing_courtyard": "ignore", 97 | "missing_footprint": "warning", 98 | "net_conflict": "warning", 99 | "nonmirrored_text_on_back_layer": "warning", 100 | "npth_inside_courtyard": "ignore", 101 | "padstack": "warning", 102 | "pth_inside_courtyard": "ignore", 103 | "shorting_items": "error", 104 | "silk_edge_clearance": "warning", 105 | "silk_over_copper": "warning", 106 | "silk_overlap": "warning", 107 | "skew_out_of_range": "error", 108 | "solder_mask_bridge": "error", 109 | "starved_thermal": "error", 110 | "text_height": "warning", 111 | "text_thickness": "warning", 112 | "through_hole_pad_without_hole": "error", 113 | "too_many_vias": "error", 114 | "track_angle": "error", 115 | "track_dangling": "warning", 116 | "track_segment_length": "error", 117 | "track_width": "error", 118 | "tracks_crossing": "error", 119 | "unconnected_items": "error", 120 | "unresolved_variable": "error", 121 | "via_dangling": "warning", 122 | "zones_intersect": "error" 123 | }, 124 | "rules": { 125 | "max_error": 0.005, 126 | "min_clearance": 0.1524, 127 | "min_connection": 0.1524, 128 | "min_copper_edge_clearance": 0.508, 129 | "min_groove_width": 0.0, 130 | "min_hole_clearance": 0.127, 131 | "min_hole_to_hole": 0.127, 132 | "min_microvia_diameter": 0.508, 133 | "min_microvia_drill": 0.254, 134 | "min_resolved_spokes": 2, 135 | "min_silk_clearance": 0.0, 136 | "min_text_height": 0.762, 137 | "min_text_thickness": 0.0254, 138 | "min_through_hole_diameter": 0.254, 139 | "min_track_width": 0.1524, 140 | "min_via_annular_width": 0.127, 141 | "min_via_diameter": 0.508, 142 | "solder_mask_to_copper_clearance": 0.0, 143 | "use_height_for_length_calcs": true 144 | }, 145 | "teardrop_options": [ 146 | { 147 | "td_onpthpad": true, 148 | "td_onroundshapesonly": false, 149 | "td_onsmdpad": true, 150 | "td_ontrackend": false, 151 | "td_onvia": true 152 | } 153 | ], 154 | "teardrop_parameters": [ 155 | { 156 | "td_allow_use_two_tracks": true, 157 | "td_curve_segcount": 0, 158 | "td_height_ratio": 1.0, 159 | "td_length_ratio": 0.5, 160 | "td_maxheight": 2.0, 161 | "td_maxlen": 1.0, 162 | "td_on_pad_in_zone": false, 163 | "td_target_name": "td_round_shape", 164 | "td_width_to_size_filter_ratio": 0.9 165 | }, 166 | { 167 | "td_allow_use_two_tracks": true, 168 | "td_curve_segcount": 0, 169 | "td_height_ratio": 1.0, 170 | "td_length_ratio": 0.5, 171 | "td_maxheight": 2.0, 172 | "td_maxlen": 1.0, 173 | "td_on_pad_in_zone": false, 174 | "td_target_name": "td_rect_shape", 175 | "td_width_to_size_filter_ratio": 0.9 176 | }, 177 | { 178 | "td_allow_use_two_tracks": true, 179 | "td_curve_segcount": 0, 180 | "td_height_ratio": 1.0, 181 | "td_length_ratio": 0.5, 182 | "td_maxheight": 2.0, 183 | "td_maxlen": 1.0, 184 | "td_on_pad_in_zone": false, 185 | "td_target_name": "td_track_end", 186 | "td_width_to_size_filter_ratio": 0.9 187 | } 188 | ], 189 | "track_widths": [ 190 | 0.0, 191 | 0.1524, 192 | 0.254, 193 | 0.508, 194 | 1.27 195 | ], 196 | "tuning_pattern_settings": { 197 | "diff_pair_defaults": { 198 | "corner_radius_percentage": 80, 199 | "corner_style": 1, 200 | "max_amplitude": 1.0, 201 | "min_amplitude": 0.2, 202 | "single_sided": false, 203 | "spacing": 1.0 204 | }, 205 | "diff_pair_skew_defaults": { 206 | "corner_radius_percentage": 80, 207 | "corner_style": 1, 208 | "max_amplitude": 1.0, 209 | "min_amplitude": 0.2, 210 | "single_sided": false, 211 | "spacing": 0.6 212 | }, 213 | "single_track_defaults": { 214 | "corner_radius_percentage": 80, 215 | "corner_style": 1, 216 | "max_amplitude": 1.0, 217 | "min_amplitude": 0.2, 218 | "single_sided": false, 219 | "spacing": 0.6 220 | } 221 | }, 222 | "via_dimensions": [ 223 | { 224 | "diameter": 0.0, 225 | "drill": 0.0 226 | } 227 | ], 228 | "zones_allow_external_fillets": false 229 | }, 230 | "ipc2581": { 231 | "dist": "", 232 | "distpn": "", 233 | "internal_id": "", 234 | "mfg": "", 235 | "mpn": "" 236 | }, 237 | "layer_pairs": [], 238 | "layer_presets": [], 239 | "viewports": [] 240 | }, 241 | "boards": [], 242 | "cvpcb": { 243 | "equivalence_files": [] 244 | }, 245 | "erc": { 246 | "erc_exclusions": [], 247 | "meta": { 248 | "version": 0 249 | }, 250 | "pin_map": [ 251 | [ 252 | 0, 253 | 0, 254 | 0, 255 | 0, 256 | 0, 257 | 0, 258 | 1, 259 | 0, 260 | 0, 261 | 0, 262 | 0, 263 | 2 264 | ], 265 | [ 266 | 0, 267 | 2, 268 | 0, 269 | 1, 270 | 0, 271 | 0, 272 | 1, 273 | 0, 274 | 2, 275 | 2, 276 | 2, 277 | 2 278 | ], 279 | [ 280 | 0, 281 | 0, 282 | 0, 283 | 0, 284 | 0, 285 | 0, 286 | 1, 287 | 0, 288 | 1, 289 | 0, 290 | 1, 291 | 2 292 | ], 293 | [ 294 | 0, 295 | 1, 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 1, 301 | 1, 302 | 2, 303 | 1, 304 | 1, 305 | 2 306 | ], 307 | [ 308 | 0, 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 1, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 2 320 | ], 321 | [ 322 | 0, 323 | 0, 324 | 0, 325 | 0, 326 | 0, 327 | 0, 328 | 0, 329 | 0, 330 | 0, 331 | 0, 332 | 0, 333 | 2 334 | ], 335 | [ 336 | 1, 337 | 1, 338 | 1, 339 | 1, 340 | 1, 341 | 0, 342 | 1, 343 | 1, 344 | 1, 345 | 1, 346 | 1, 347 | 2 348 | ], 349 | [ 350 | 0, 351 | 0, 352 | 0, 353 | 1, 354 | 0, 355 | 0, 356 | 1, 357 | 0, 358 | 0, 359 | 0, 360 | 0, 361 | 2 362 | ], 363 | [ 364 | 0, 365 | 2, 366 | 1, 367 | 2, 368 | 0, 369 | 0, 370 | 1, 371 | 0, 372 | 2, 373 | 2, 374 | 2, 375 | 2 376 | ], 377 | [ 378 | 0, 379 | 2, 380 | 0, 381 | 1, 382 | 0, 383 | 0, 384 | 1, 385 | 0, 386 | 2, 387 | 0, 388 | 0, 389 | 2 390 | ], 391 | [ 392 | 0, 393 | 2, 394 | 1, 395 | 1, 396 | 0, 397 | 0, 398 | 1, 399 | 0, 400 | 2, 401 | 0, 402 | 0, 403 | 2 404 | ], 405 | [ 406 | 2, 407 | 2, 408 | 2, 409 | 2, 410 | 2, 411 | 2, 412 | 2, 413 | 2, 414 | 2, 415 | 2, 416 | 2, 417 | 2 418 | ] 419 | ], 420 | "rule_severities": { 421 | "bus_definition_conflict": "error", 422 | "bus_entry_needed": "error", 423 | "bus_to_bus_conflict": "error", 424 | "bus_to_net_conflict": "error", 425 | "conflicting_netclasses": "error", 426 | "different_unit_footprint": "error", 427 | "different_unit_net": "error", 428 | "duplicate_reference": "error", 429 | "duplicate_sheet_names": "error", 430 | "endpoint_off_grid": "warning", 431 | "extra_units": "error", 432 | "footprint_filter": "ignore", 433 | "footprint_link_issues": "warning", 434 | "four_way_junction": "ignore", 435 | "global_label_dangling": "warning", 436 | "hier_label_mismatch": "error", 437 | "label_dangling": "error", 438 | "label_multiple_wires": "warning", 439 | "lib_symbol_issues": "warning", 440 | "lib_symbol_mismatch": "warning", 441 | "missing_bidi_pin": "warning", 442 | "missing_input_pin": "warning", 443 | "missing_power_pin": "error", 444 | "missing_unit": "warning", 445 | "multiple_net_names": "warning", 446 | "net_not_bus_member": "warning", 447 | "no_connect_connected": "warning", 448 | "no_connect_dangling": "warning", 449 | "pin_not_connected": "error", 450 | "pin_not_driven": "error", 451 | "pin_to_pin": "warning", 452 | "power_pin_not_driven": "error", 453 | "same_local_global_label": "warning", 454 | "similar_label_and_power": "warning", 455 | "similar_labels": "warning", 456 | "similar_power": "warning", 457 | "simulation_model_issue": "ignore", 458 | "single_global_label": "ignore", 459 | "unannotated": "error", 460 | "unconnected_wire_endpoint": "warning", 461 | "unit_value_mismatch": "error", 462 | "unresolved_variable": "error", 463 | "wire_dangling": "error" 464 | } 465 | }, 466 | "libraries": { 467 | "pinned_footprint_libs": [], 468 | "pinned_symbol_libs": [] 469 | }, 470 | "meta": { 471 | "filename": "sensor.kicad_pro", 472 | "version": 3 473 | }, 474 | "net_settings": { 475 | "classes": [ 476 | { 477 | "bus_width": 12, 478 | "clearance": 0.2, 479 | "diff_pair_gap": 0.25, 480 | "diff_pair_via_gap": 0.25, 481 | "diff_pair_width": 0.2, 482 | "line_style": 0, 483 | "microvia_diameter": 0.3, 484 | "microvia_drill": 0.1, 485 | "name": "Default", 486 | "pcb_color": "rgba(0, 0, 0, 0.000)", 487 | "priority": 2147483647, 488 | "schematic_color": "rgba(0, 0, 0, 0.000)", 489 | "track_width": 0.2, 490 | "via_diameter": 0.6, 491 | "via_drill": 0.3, 492 | "wire_width": 6 493 | } 494 | ], 495 | "meta": { 496 | "version": 4 497 | }, 498 | "net_colors": null, 499 | "netclass_assignments": null, 500 | "netclass_patterns": [] 501 | }, 502 | "pcbnew": { 503 | "last_paths": { 504 | "gencad": "", 505 | "idf": "", 506 | "netlist": "", 507 | "plot": "", 508 | "pos_files": "", 509 | "specctra_dsn": "", 510 | "step": "", 511 | "svg": "", 512 | "vrml": "" 513 | }, 514 | "page_layout_descr_file": "" 515 | }, 516 | "schematic": { 517 | "annotate_start_num": 0, 518 | "bom_export_filename": "", 519 | "bom_fmt_presets": [], 520 | "bom_fmt_settings": { 521 | "field_delimiter": ",", 522 | "keep_line_breaks": false, 523 | "keep_tabs": false, 524 | "name": "CSV", 525 | "ref_delimiter": ",", 526 | "ref_range_delimiter": "", 527 | "string_delimiter": "\"" 528 | }, 529 | "bom_presets": [], 530 | "bom_settings": { 531 | "exclude_dnp": false, 532 | "fields_ordered": [ 533 | { 534 | "group_by": false, 535 | "label": "Reference", 536 | "name": "Reference", 537 | "show": true 538 | }, 539 | { 540 | "group_by": true, 541 | "label": "Value", 542 | "name": "Value", 543 | "show": true 544 | }, 545 | { 546 | "group_by": false, 547 | "label": "Datasheet", 548 | "name": "Datasheet", 549 | "show": true 550 | }, 551 | { 552 | "group_by": false, 553 | "label": "Footprint", 554 | "name": "Footprint", 555 | "show": true 556 | }, 557 | { 558 | "group_by": false, 559 | "label": "Qty", 560 | "name": "${QUANTITY}", 561 | "show": true 562 | }, 563 | { 564 | "group_by": true, 565 | "label": "DNP", 566 | "name": "${DNP}", 567 | "show": true 568 | } 569 | ], 570 | "filter_string": "", 571 | "group_symbols": true, 572 | "include_excluded_from_bom": false, 573 | "name": "Grouped By Value", 574 | "sort_asc": true, 575 | "sort_field": "Reference" 576 | }, 577 | "connection_grid_size": 50.0, 578 | "drawing": { 579 | "dashed_lines_dash_length_ratio": 12.0, 580 | "dashed_lines_gap_length_ratio": 3.0, 581 | "default_line_thickness": 6.0, 582 | "default_text_size": 50.0, 583 | "field_names": [], 584 | "intersheets_ref_own_page": false, 585 | "intersheets_ref_prefix": "", 586 | "intersheets_ref_short": false, 587 | "intersheets_ref_show": false, 588 | "intersheets_ref_suffix": "", 589 | "junction_size_choice": 3, 590 | "label_size_ratio": 0.375, 591 | "operating_point_overlay_i_precision": 3, 592 | "operating_point_overlay_i_range": "~A", 593 | "operating_point_overlay_v_precision": 3, 594 | "operating_point_overlay_v_range": "~V", 595 | "overbar_offset_ratio": 1.23, 596 | "pin_symbol_size": 25.0, 597 | "text_offset_ratio": 0.15 598 | }, 599 | "legacy_lib_dir": "", 600 | "legacy_lib_list": [], 601 | "meta": { 602 | "version": 1 603 | }, 604 | "net_format_name": "", 605 | "page_layout_descr_file": "", 606 | "plot_directory": "", 607 | "space_save_all_events": true, 608 | "spice_current_sheet_as_root": false, 609 | "spice_external_command": "spice \"%I\"", 610 | "spice_model_current_sheet_as_root": true, 611 | "spice_save_all_currents": false, 612 | "spice_save_all_dissipations": false, 613 | "spice_save_all_voltages": false, 614 | "subpart_first_id": 65, 615 | "subpart_id_separator": 0 616 | }, 617 | "sheets": [ 618 | [ 619 | "a8eb6c23-d45b-43b3-ae70-e0472c6c012f", 620 | "Root" 621 | ] 622 | ], 623 | "text_variables": {} 624 | } 625 | -------------------------------------------------------------------------------- /crank/sensor2/sensor2.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "3dviewports": [], 4 | "design_settings": { 5 | "defaults": { 6 | "apply_defaults_to_fp_fields": false, 7 | "apply_defaults_to_fp_shapes": false, 8 | "apply_defaults_to_fp_text": false, 9 | "board_outline_line_width": 0.05, 10 | "copper_line_width": 0.2, 11 | "copper_text_italic": false, 12 | "copper_text_size_h": 1.5, 13 | "copper_text_size_v": 1.5, 14 | "copper_text_thickness": 0.3, 15 | "copper_text_upright": false, 16 | "courtyard_line_width": 0.05, 17 | "dimension_precision": 4, 18 | "dimension_units": 3, 19 | "dimensions": { 20 | "arrow_length": 1270000, 21 | "extension_offset": 500000, 22 | "keep_text_aligned": true, 23 | "suppress_zeroes": false, 24 | "text_position": 0, 25 | "units_format": 1 26 | }, 27 | "fab_line_width": 0.1, 28 | "fab_text_italic": false, 29 | "fab_text_size_h": 1.0, 30 | "fab_text_size_v": 1.0, 31 | "fab_text_thickness": 0.15, 32 | "fab_text_upright": false, 33 | "other_line_width": 0.1, 34 | "other_text_italic": false, 35 | "other_text_size_h": 1.0, 36 | "other_text_size_v": 1.0, 37 | "other_text_thickness": 0.15, 38 | "other_text_upright": false, 39 | "pads": { 40 | "drill": 0.762, 41 | "height": 1.524, 42 | "width": 1.524 43 | }, 44 | "silk_line_width": 0.1, 45 | "silk_text_italic": false, 46 | "silk_text_size_h": 0.762, 47 | "silk_text_size_v": 0.762, 48 | "silk_text_thickness": 0.1, 49 | "silk_text_upright": false, 50 | "zones": { 51 | "min_clearance": 0.5 52 | } 53 | }, 54 | "diff_pair_dimensions": [ 55 | { 56 | "gap": 0.0, 57 | "via_gap": 0.0, 58 | "width": 0.0 59 | } 60 | ], 61 | "drc_exclusions": [], 62 | "meta": { 63 | "version": 2 64 | }, 65 | "rule_severities": { 66 | "annular_width": "error", 67 | "clearance": "error", 68 | "connection_width": "warning", 69 | "copper_edge_clearance": "error", 70 | "copper_sliver": "warning", 71 | "courtyards_overlap": "error", 72 | "creepage": "error", 73 | "diff_pair_gap_out_of_range": "error", 74 | "diff_pair_uncoupled_length_too_long": "error", 75 | "drill_out_of_range": "error", 76 | "duplicate_footprints": "warning", 77 | "extra_footprint": "warning", 78 | "footprint": "error", 79 | "footprint_filters_mismatch": "ignore", 80 | "footprint_symbol_mismatch": "warning", 81 | "footprint_type_mismatch": "ignore", 82 | "hole_clearance": "error", 83 | "hole_near_hole": "error", 84 | "hole_to_hole": "error", 85 | "holes_co_located": "warning", 86 | "invalid_outline": "error", 87 | "isolated_copper": "warning", 88 | "item_on_disabled_layer": "error", 89 | "items_not_allowed": "error", 90 | "length_out_of_range": "error", 91 | "lib_footprint_issues": "warning", 92 | "lib_footprint_mismatch": "warning", 93 | "malformed_courtyard": "error", 94 | "microvia_drill_out_of_range": "error", 95 | "mirrored_text_on_front_layer": "warning", 96 | "missing_courtyard": "ignore", 97 | "missing_footprint": "warning", 98 | "net_conflict": "warning", 99 | "nonmirrored_text_on_back_layer": "warning", 100 | "npth_inside_courtyard": "ignore", 101 | "padstack": "warning", 102 | "pth_inside_courtyard": "ignore", 103 | "shorting_items": "error", 104 | "silk_edge_clearance": "warning", 105 | "silk_over_copper": "warning", 106 | "silk_overlap": "warning", 107 | "skew_out_of_range": "error", 108 | "solder_mask_bridge": "error", 109 | "starved_thermal": "error", 110 | "text_height": "warning", 111 | "text_thickness": "warning", 112 | "through_hole_pad_without_hole": "error", 113 | "too_many_vias": "error", 114 | "track_angle": "error", 115 | "track_dangling": "warning", 116 | "track_segment_length": "error", 117 | "track_width": "error", 118 | "tracks_crossing": "error", 119 | "unconnected_items": "error", 120 | "unresolved_variable": "error", 121 | "via_dangling": "warning", 122 | "zones_intersect": "error" 123 | }, 124 | "rules": { 125 | "max_error": 0.005, 126 | "min_clearance": 0.1524, 127 | "min_connection": 0.1524, 128 | "min_copper_edge_clearance": 0.508, 129 | "min_groove_width": 0.0, 130 | "min_hole_clearance": 0.127, 131 | "min_hole_to_hole": 0.127, 132 | "min_microvia_diameter": 0.508, 133 | "min_microvia_drill": 0.254, 134 | "min_resolved_spokes": 2, 135 | "min_silk_clearance": 0.0, 136 | "min_text_height": 0.762, 137 | "min_text_thickness": 0.0254, 138 | "min_through_hole_diameter": 0.254, 139 | "min_track_width": 0.1524, 140 | "min_via_annular_width": 0.127, 141 | "min_via_diameter": 0.508, 142 | "solder_mask_to_copper_clearance": 0.0, 143 | "use_height_for_length_calcs": true 144 | }, 145 | "teardrop_options": [ 146 | { 147 | "td_onpthpad": true, 148 | "td_onroundshapesonly": false, 149 | "td_onsmdpad": true, 150 | "td_ontrackend": false, 151 | "td_onvia": true 152 | } 153 | ], 154 | "teardrop_parameters": [ 155 | { 156 | "td_allow_use_two_tracks": true, 157 | "td_curve_segcount": 0, 158 | "td_height_ratio": 1.0, 159 | "td_length_ratio": 0.5, 160 | "td_maxheight": 2.0, 161 | "td_maxlen": 1.0, 162 | "td_on_pad_in_zone": false, 163 | "td_target_name": "td_round_shape", 164 | "td_width_to_size_filter_ratio": 0.9 165 | }, 166 | { 167 | "td_allow_use_two_tracks": true, 168 | "td_curve_segcount": 0, 169 | "td_height_ratio": 1.0, 170 | "td_length_ratio": 0.5, 171 | "td_maxheight": 2.0, 172 | "td_maxlen": 1.0, 173 | "td_on_pad_in_zone": false, 174 | "td_target_name": "td_rect_shape", 175 | "td_width_to_size_filter_ratio": 0.9 176 | }, 177 | { 178 | "td_allow_use_two_tracks": true, 179 | "td_curve_segcount": 0, 180 | "td_height_ratio": 1.0, 181 | "td_length_ratio": 0.5, 182 | "td_maxheight": 2.0, 183 | "td_maxlen": 1.0, 184 | "td_on_pad_in_zone": false, 185 | "td_target_name": "td_track_end", 186 | "td_width_to_size_filter_ratio": 0.9 187 | } 188 | ], 189 | "track_widths": [ 190 | 0.0, 191 | 0.1524, 192 | 0.254, 193 | 0.635, 194 | 1.27 195 | ], 196 | "tuning_pattern_settings": { 197 | "diff_pair_defaults": { 198 | "corner_radius_percentage": 80, 199 | "corner_style": 1, 200 | "max_amplitude": 1.0, 201 | "min_amplitude": 0.2, 202 | "single_sided": false, 203 | "spacing": 1.0 204 | }, 205 | "diff_pair_skew_defaults": { 206 | "corner_radius_percentage": 80, 207 | "corner_style": 1, 208 | "max_amplitude": 1.0, 209 | "min_amplitude": 0.2, 210 | "single_sided": false, 211 | "spacing": 0.6 212 | }, 213 | "single_track_defaults": { 214 | "corner_radius_percentage": 80, 215 | "corner_style": 1, 216 | "max_amplitude": 1.0, 217 | "min_amplitude": 0.2, 218 | "single_sided": false, 219 | "spacing": 0.6 220 | } 221 | }, 222 | "via_dimensions": [ 223 | { 224 | "diameter": 0.0, 225 | "drill": 0.0 226 | } 227 | ], 228 | "zones_allow_external_fillets": false 229 | }, 230 | "ipc2581": { 231 | "dist": "", 232 | "distpn": "", 233 | "internal_id": "", 234 | "mfg": "", 235 | "mpn": "" 236 | }, 237 | "layer_pairs": [], 238 | "layer_presets": [], 239 | "viewports": [] 240 | }, 241 | "boards": [], 242 | "cvpcb": { 243 | "equivalence_files": [] 244 | }, 245 | "erc": { 246 | "erc_exclusions": [], 247 | "meta": { 248 | "version": 0 249 | }, 250 | "pin_map": [ 251 | [ 252 | 0, 253 | 0, 254 | 0, 255 | 0, 256 | 0, 257 | 0, 258 | 1, 259 | 0, 260 | 0, 261 | 0, 262 | 0, 263 | 2 264 | ], 265 | [ 266 | 0, 267 | 2, 268 | 0, 269 | 1, 270 | 0, 271 | 0, 272 | 1, 273 | 0, 274 | 2, 275 | 2, 276 | 2, 277 | 2 278 | ], 279 | [ 280 | 0, 281 | 0, 282 | 0, 283 | 0, 284 | 0, 285 | 0, 286 | 1, 287 | 0, 288 | 1, 289 | 0, 290 | 1, 291 | 2 292 | ], 293 | [ 294 | 0, 295 | 1, 296 | 0, 297 | 0, 298 | 0, 299 | 0, 300 | 1, 301 | 1, 302 | 2, 303 | 1, 304 | 1, 305 | 2 306 | ], 307 | [ 308 | 0, 309 | 0, 310 | 0, 311 | 0, 312 | 0, 313 | 0, 314 | 1, 315 | 0, 316 | 0, 317 | 0, 318 | 0, 319 | 2 320 | ], 321 | [ 322 | 0, 323 | 0, 324 | 0, 325 | 0, 326 | 0, 327 | 0, 328 | 0, 329 | 0, 330 | 0, 331 | 0, 332 | 0, 333 | 2 334 | ], 335 | [ 336 | 1, 337 | 1, 338 | 1, 339 | 1, 340 | 1, 341 | 0, 342 | 1, 343 | 1, 344 | 1, 345 | 1, 346 | 1, 347 | 2 348 | ], 349 | [ 350 | 0, 351 | 0, 352 | 0, 353 | 1, 354 | 0, 355 | 0, 356 | 1, 357 | 0, 358 | 0, 359 | 0, 360 | 0, 361 | 2 362 | ], 363 | [ 364 | 0, 365 | 2, 366 | 1, 367 | 2, 368 | 0, 369 | 0, 370 | 1, 371 | 0, 372 | 2, 373 | 2, 374 | 2, 375 | 2 376 | ], 377 | [ 378 | 0, 379 | 2, 380 | 0, 381 | 1, 382 | 0, 383 | 0, 384 | 1, 385 | 0, 386 | 2, 387 | 0, 388 | 0, 389 | 2 390 | ], 391 | [ 392 | 0, 393 | 2, 394 | 1, 395 | 1, 396 | 0, 397 | 0, 398 | 1, 399 | 0, 400 | 2, 401 | 0, 402 | 0, 403 | 2 404 | ], 405 | [ 406 | 2, 407 | 2, 408 | 2, 409 | 2, 410 | 2, 411 | 2, 412 | 2, 413 | 2, 414 | 2, 415 | 2, 416 | 2, 417 | 2 418 | ] 419 | ], 420 | "rule_severities": { 421 | "bus_definition_conflict": "error", 422 | "bus_entry_needed": "error", 423 | "bus_to_bus_conflict": "error", 424 | "bus_to_net_conflict": "error", 425 | "conflicting_netclasses": "error", 426 | "different_unit_footprint": "error", 427 | "different_unit_net": "error", 428 | "duplicate_reference": "error", 429 | "duplicate_sheet_names": "error", 430 | "endpoint_off_grid": "warning", 431 | "extra_units": "error", 432 | "footprint_filter": "ignore", 433 | "footprint_link_issues": "warning", 434 | "four_way_junction": "ignore", 435 | "global_label_dangling": "warning", 436 | "hier_label_mismatch": "error", 437 | "label_dangling": "error", 438 | "label_multiple_wires": "warning", 439 | "lib_symbol_issues": "warning", 440 | "lib_symbol_mismatch": "warning", 441 | "missing_bidi_pin": "warning", 442 | "missing_input_pin": "warning", 443 | "missing_power_pin": "error", 444 | "missing_unit": "warning", 445 | "multiple_net_names": "warning", 446 | "net_not_bus_member": "warning", 447 | "no_connect_connected": "warning", 448 | "no_connect_dangling": "warning", 449 | "pin_not_connected": "error", 450 | "pin_not_driven": "error", 451 | "pin_to_pin": "warning", 452 | "power_pin_not_driven": "error", 453 | "same_local_global_label": "warning", 454 | "similar_label_and_power": "warning", 455 | "similar_labels": "warning", 456 | "similar_power": "warning", 457 | "simulation_model_issue": "ignore", 458 | "single_global_label": "ignore", 459 | "unannotated": "error", 460 | "unconnected_wire_endpoint": "warning", 461 | "unit_value_mismatch": "error", 462 | "unresolved_variable": "error", 463 | "wire_dangling": "error" 464 | } 465 | }, 466 | "libraries": { 467 | "pinned_footprint_libs": [], 468 | "pinned_symbol_libs": [] 469 | }, 470 | "meta": { 471 | "filename": "sensor2.kicad_pro", 472 | "version": 3 473 | }, 474 | "net_settings": { 475 | "classes": [ 476 | { 477 | "bus_width": 12, 478 | "clearance": 0.2, 479 | "diff_pair_gap": 0.25, 480 | "diff_pair_via_gap": 0.25, 481 | "diff_pair_width": 0.2, 482 | "line_style": 0, 483 | "microvia_diameter": 0.3, 484 | "microvia_drill": 0.1, 485 | "name": "Default", 486 | "pcb_color": "rgba(0, 0, 0, 0.000)", 487 | "priority": 2147483647, 488 | "schematic_color": "rgba(0, 0, 0, 0.000)", 489 | "track_width": 0.2, 490 | "via_diameter": 0.6, 491 | "via_drill": 0.3, 492 | "wire_width": 6 493 | } 494 | ], 495 | "meta": { 496 | "version": 4 497 | }, 498 | "net_colors": null, 499 | "netclass_assignments": null, 500 | "netclass_patterns": [] 501 | }, 502 | "pcbnew": { 503 | "last_paths": { 504 | "gencad": "", 505 | "idf": "", 506 | "netlist": "", 507 | "plot": "", 508 | "pos_files": "", 509 | "specctra_dsn": "", 510 | "step": "", 511 | "svg": "", 512 | "vrml": "" 513 | }, 514 | "page_layout_descr_file": "" 515 | }, 516 | "schematic": { 517 | "annotate_start_num": 0, 518 | "bom_export_filename": "", 519 | "bom_fmt_presets": [], 520 | "bom_fmt_settings": { 521 | "field_delimiter": ",", 522 | "keep_line_breaks": false, 523 | "keep_tabs": false, 524 | "name": "CSV", 525 | "ref_delimiter": ",", 526 | "ref_range_delimiter": "", 527 | "string_delimiter": "\"" 528 | }, 529 | "bom_presets": [], 530 | "bom_settings": { 531 | "exclude_dnp": false, 532 | "fields_ordered": [ 533 | { 534 | "group_by": false, 535 | "label": "Reference", 536 | "name": "Reference", 537 | "show": true 538 | }, 539 | { 540 | "group_by": true, 541 | "label": "Value", 542 | "name": "Value", 543 | "show": true 544 | }, 545 | { 546 | "group_by": false, 547 | "label": "Datasheet", 548 | "name": "Datasheet", 549 | "show": true 550 | }, 551 | { 552 | "group_by": false, 553 | "label": "Footprint", 554 | "name": "Footprint", 555 | "show": true 556 | }, 557 | { 558 | "group_by": false, 559 | "label": "Qty", 560 | "name": "${QUANTITY}", 561 | "show": true 562 | }, 563 | { 564 | "group_by": true, 565 | "label": "DNP", 566 | "name": "${DNP}", 567 | "show": true 568 | } 569 | ], 570 | "filter_string": "", 571 | "group_symbols": true, 572 | "include_excluded_from_bom": false, 573 | "name": "Grouped By Value", 574 | "sort_asc": true, 575 | "sort_field": "Reference" 576 | }, 577 | "connection_grid_size": 50.0, 578 | "drawing": { 579 | "dashed_lines_dash_length_ratio": 12.0, 580 | "dashed_lines_gap_length_ratio": 3.0, 581 | "default_line_thickness": 6.0, 582 | "default_text_size": 50.0, 583 | "field_names": [], 584 | "intersheets_ref_own_page": false, 585 | "intersheets_ref_prefix": "", 586 | "intersheets_ref_short": false, 587 | "intersheets_ref_show": false, 588 | "intersheets_ref_suffix": "", 589 | "junction_size_choice": 3, 590 | "label_size_ratio": 0.375, 591 | "operating_point_overlay_i_precision": 3, 592 | "operating_point_overlay_i_range": "~A", 593 | "operating_point_overlay_v_precision": 3, 594 | "operating_point_overlay_v_range": "~V", 595 | "overbar_offset_ratio": 1.23, 596 | "pin_symbol_size": 25.0, 597 | "text_offset_ratio": 0.15 598 | }, 599 | "legacy_lib_dir": "", 600 | "legacy_lib_list": [], 601 | "meta": { 602 | "version": 1 603 | }, 604 | "net_format_name": "", 605 | "page_layout_descr_file": "", 606 | "plot_directory": "", 607 | "space_save_all_events": true, 608 | "spice_current_sheet_as_root": false, 609 | "spice_external_command": "spice \"%I\"", 610 | "spice_model_current_sheet_as_root": true, 611 | "spice_save_all_currents": false, 612 | "spice_save_all_dissipations": false, 613 | "spice_save_all_voltages": false, 614 | "subpart_first_id": 65, 615 | "subpart_id_separator": 0 616 | }, 617 | "sheets": [ 618 | [ 619 | "a8eb6c23-d45b-43b3-ae70-e0472c6c012f", 620 | "Root" 621 | ] 622 | ], 623 | "text_variables": {} 624 | } 625 | -------------------------------------------------------------------------------- /colorknot/luaglue.c: -------------------------------------------------------------------------------- 1 | // 2 | // 3d-glue.c 3 | // Extension 4 | // 5 | // Created by Dave Hayden on 9/19/15. 6 | // Copyright © 2015 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #include 10 | #include "luaglue.h" 11 | #include "mini3d.h" 12 | #include "3dmath.h" 13 | #include "shape.h" 14 | #include "scene.h" 15 | 16 | static PlaydateAPI* pd = NULL; 17 | 18 | static void* get3DObj(int n, char* type) 19 | { 20 | void* obj = pd->lua->getArgObject(n, type, NULL); 21 | 22 | if ( obj == NULL ) 23 | pd->system->error("object of type %s not found at stack position %i", type, n); 24 | 25 | return obj; 26 | } 27 | 28 | static Scene3D* getScene(int n) { return get3DObj(n, "lib3d.scene"); } 29 | static Scene3DNode* getSceneNode(int n) { return get3DObj(n, "lib3d.scenenode"); } 30 | static Shape3D* getShape(int n) { return get3DObj(n, "lib3d.shape"); } 31 | static Point3D* getPoint(int n) { return get3DObj(n, "lib3d.point"); } 32 | static Matrix3D* getMatrix(int n) { return get3DObj(n, "lib3d.matrix"); } 33 | 34 | /// Scene 35 | 36 | static int scene_new(lua_State* L) 37 | { 38 | Scene3D* scene = m3d_malloc(sizeof(Scene3D)); 39 | Scene3D_init(scene); 40 | pd->lua->pushObject(scene, "lib3d.scene", 0); 41 | return 1; 42 | } 43 | 44 | static int scene_gc(lua_State* L) 45 | { 46 | Scene3D* scene = getScene(1); 47 | Scene3D_deinit(scene); 48 | m3d_free(scene); 49 | return 0; 50 | } 51 | 52 | static int scene_getRoot(lua_State* L) 53 | { 54 | Scene3D* scene = getScene(1); 55 | pd->lua->pushObject(Scene3D_getRootNode(scene), "lib3d.scenenode", 0); 56 | return 1; 57 | } 58 | 59 | uint8_t frame[120*200]; 60 | 61 | static int scene_draw(lua_State* L) 62 | { 63 | Scene3D* scene = getScene(1); 64 | uint8_t* bitmap = pd->graphics->getFrame(); 65 | 66 | memset(frame,0,120*200); 67 | Scene3D_draw(scene, frame, 200); 68 | 69 | for ( int y = 0; y < 120; ++y ) 70 | { 71 | uint8_t rowbuf[400]; 72 | uint8_t* framep = &frame[y*200]; 73 | 74 | for ( int x = 0; x < 50; ++x ) 75 | { 76 | uint8_t c1 = *framep++; 77 | uint8_t c2 = *framep++; 78 | uint8_t c3 = *framep++; 79 | uint8_t c4 = *framep++; 80 | 81 | *bitmap = (c1 & 0xc) << 4 | (c2 & 0xc) << 2 | (c3 & 0xc) | (c4 & 0xc) >> 2; 82 | *(bitmap+52) = (c1 & 0x3) << 6 | (c2 & 0x3) << 4 | (c3 & 0x3) << 2 | (c4 & 0x3); 83 | ++bitmap; 84 | } 85 | 86 | bitmap += 2 + 52; 87 | } 88 | 89 | pd->graphics->markUpdatedRows(0, 239); // XXX 90 | 91 | return 0; 92 | } 93 | 94 | static int scene_drawNode(lua_State* L) 95 | { 96 | Scene3D* scene = getScene(1); 97 | Scene3DNode* node = getSceneNode(2); 98 | 99 | Scene3D_drawNode(scene, node, pd->graphics->getFrame(), LCD_ROWSIZE); 100 | pd->graphics->markUpdatedRows(0, LCD_ROWS-1); // XXX 101 | 102 | return 0; 103 | } 104 | 105 | static int scene_setLight(lua_State* L) 106 | { 107 | Scene3D* scene = getScene(1); 108 | float x = pd->lua->getArgFloat(2); 109 | float y = pd->lua->getArgFloat(3); 110 | float z = pd->lua->getArgFloat(4); 111 | 112 | Scene3D_setGlobalLight(scene, Vector3DMake(x, y, z)); 113 | 114 | return 0; 115 | } 116 | 117 | static int scene_setCenter(lua_State* L) 118 | { 119 | Scene3D* scene = getScene(1); 120 | float x = pd->lua->getArgFloat(2); 121 | float y = pd->lua->getArgFloat(3); 122 | 123 | Scene3D_setCenter(scene, x, y); 124 | 125 | return 0; 126 | } 127 | 128 | static Point3D cameraOrigin = { .x = 0, .y = 0, .z = -1 }; 129 | static Point3D cameraLookat = { .x = 0, .y = 0, .z = 0 }; 130 | static float cameraScale = 1.0; 131 | static Vector3D cameraUp = { .dx = 0, .dy = -1, .dz = 0 }; 132 | 133 | static int scene_setCameraOrigin(lua_State* L) 134 | { 135 | Scene3D* scene = getScene(1); 136 | cameraOrigin.x = pd->lua->getArgFloat(2); 137 | cameraOrigin.y = pd->lua->getArgFloat(3); 138 | cameraOrigin.z = pd->lua->getArgFloat(4); 139 | 140 | Scene3D_setCamera(scene, cameraOrigin, cameraLookat, cameraScale, cameraUp); 141 | 142 | return 0; 143 | } 144 | 145 | static int scene_setCameraTarget(lua_State* L) 146 | { 147 | Scene3D* scene = getScene(1); 148 | cameraLookat.x = pd->lua->getArgFloat(2); 149 | cameraLookat.y = pd->lua->getArgFloat(3); 150 | cameraLookat.z = pd->lua->getArgFloat(4); 151 | 152 | Scene3D_setCamera(scene, cameraOrigin, cameraLookat, cameraScale, cameraUp); 153 | 154 | return 0; 155 | } 156 | 157 | static int scene_setCameraScale(lua_State* L) 158 | { 159 | Scene3D* scene = getScene(1); 160 | cameraScale = pd->lua->getArgFloat(2); 161 | 162 | Scene3D_setCamera(scene, cameraOrigin, cameraLookat, cameraScale, cameraUp); 163 | 164 | return 0; 165 | } 166 | 167 | static int scene_setCameraUp(lua_State* L) 168 | { 169 | Scene3D* scene = getScene(1); 170 | cameraUp.dx = pd->lua->getArgFloat(2); 171 | cameraUp.dy = pd->lua->getArgFloat(3); 172 | cameraUp.dz = pd->lua->getArgFloat(4); 173 | 174 | Scene3D_setCamera(scene, cameraOrigin, cameraLookat, cameraScale, cameraUp); 175 | 176 | return 0; 177 | } 178 | 179 | static const lua_reg lib3DScene[] = 180 | { 181 | { "__gc", scene_gc }, 182 | { "new", scene_new }, 183 | { "draw", scene_draw }, 184 | { "drawNode", scene_drawNode }, 185 | { "getRootNode", scene_getRoot }, 186 | { "setLight", scene_setLight }, 187 | { "setCenter", scene_setCenter }, 188 | { "setCameraOrigin", scene_setCameraOrigin }, 189 | { "setCameraScale", scene_setCameraScale }, 190 | { "setCameraTarget", scene_setCameraTarget }, 191 | { "setCameraUp", scene_setCameraUp }, 192 | { NULL, NULL } 193 | }; 194 | 195 | 196 | /// Node 197 | 198 | static int node_gc(lua_State* L) 199 | { 200 | // XXX - release shapes 201 | return 0; 202 | } 203 | 204 | static int node_addShape(lua_State* L) 205 | { 206 | Scene3DNode* node = getSceneNode(1); 207 | Shape3D* shape = getShape(2); 208 | 209 | Matrix3D* m = pd->lua->getArgObject(3, "lib3d.matrix", NULL); 210 | 211 | if ( m != NULL ) 212 | Scene3DNode_addShapeWithTransform(node, shape, *m); 213 | 214 | else if ( pd->lua->getArgCount() > 2 ) 215 | { 216 | Vector3D v = { 0, 0, 0 }; 217 | v.dx = pd->lua->getArgFloat(3); 218 | v.dy = pd->lua->getArgFloat(4); 219 | v.dz = pd->lua->getArgFloat(5); 220 | 221 | Scene3DNode_addShapeWithOffset(node, shape, v); 222 | } 223 | else 224 | Scene3DNode_addShape(node, shape); 225 | 226 | //pd->lua->retainObject(shapeobj); 227 | 228 | return 0; 229 | } 230 | 231 | static int node_makeChildNode(lua_State* L) 232 | { 233 | Scene3DNode* node = getSceneNode(1); 234 | Scene3DNode* scene = Scene3DNode_newChild(node); 235 | 236 | pd->lua->pushObject(scene, "lib3d.scenenode", 0); 237 | 238 | return 1; 239 | } 240 | 241 | static int node_setTransform(lua_State* L) 242 | { 243 | Scene3DNode* node = getSceneNode(1); 244 | Matrix3D* xform = getMatrix(2); 245 | 246 | Scene3DNode_setTransform(node, xform); 247 | 248 | return 0; 249 | } 250 | 251 | static int node_addTransform(lua_State* L) 252 | { 253 | Scene3DNode* node = getSceneNode(1); 254 | Matrix3D* xform = getMatrix(2); 255 | 256 | Scene3DNode_addTransform(node, xform); 257 | 258 | return 0; 259 | } 260 | 261 | static int node_translateBy(lua_State* L) 262 | { 263 | Scene3DNode* node = getSceneNode(1); 264 | 265 | Matrix3D xform = identityMatrix; 266 | xform.dx = pd->lua->getArgFloat(2); 267 | xform.dy = pd->lua->getArgFloat(3); 268 | xform.dz = pd->lua->getArgFloat(4); 269 | 270 | Scene3DNode_addTransform(node, &xform); 271 | 272 | return 0; 273 | } 274 | 275 | static int node_scaleBy(lua_State* L) 276 | { 277 | Scene3DNode* node = getSceneNode(1); 278 | Matrix3D xform = node->transform; 279 | float sx, sy, sz; 280 | 281 | if ( pd->lua->getArgCount() > 2 ) 282 | { 283 | sx = pd->lua->getArgFloat(2); 284 | sy = pd->lua->getArgFloat(3); 285 | sz = pd->lua->getArgFloat(4); 286 | } 287 | else 288 | sx = sy = sz = pd->lua->getArgFloat(2); 289 | 290 | xform.isIdentity = 0; 291 | 292 | xform.m[0][0] *= sx; 293 | xform.m[1][0] *= sx; 294 | xform.m[2][0] *= sx; 295 | 296 | xform.m[0][1] *= sy; 297 | xform.m[1][1] *= sy; 298 | xform.m[2][1] *= sy; 299 | 300 | xform.m[0][2] *= sz; 301 | xform.m[1][2] *= sz; 302 | xform.m[2][2] *= sz; 303 | 304 | xform.dx *= sx; 305 | xform.dy *= sy; 306 | xform.dz *= sz; 307 | 308 | xform.inverting = (sx * sy * sz < 0); 309 | 310 | Scene3DNode_setTransform(node, &xform); 311 | 312 | return 0; 313 | } 314 | 315 | static int node_setColorBias(lua_State* L) 316 | { 317 | Scene3DNode* node = getSceneNode(1); 318 | 319 | Scene3DNode_setColorBias(node, pd->lua->getArgFloat(2)); 320 | 321 | return 0; 322 | } 323 | 324 | static int node_setFilled(lua_State* L) 325 | { 326 | Scene3DNode* node = getSceneNode(1); 327 | int flag = pd->lua->getArgBool(2); 328 | 329 | RenderStyle style = Scene3DNode_getRenderStyle(node); 330 | 331 | if ( flag ) 332 | style |= kRenderFilled; 333 | else 334 | style &= ~kRenderFilled; 335 | 336 | Scene3DNode_setRenderStyle(node, style); 337 | 338 | return 0; 339 | } 340 | 341 | static int node_setWireframeMode(lua_State* L) 342 | { 343 | Scene3DNode* node = getSceneNode(1); 344 | int mode = pd->lua->getArgInt(2); 345 | 346 | RenderStyle style = Scene3DNode_getRenderStyle(node); 347 | 348 | if ( mode > 0 ) 349 | { 350 | style |= kRenderWireframe; 351 | 352 | if ( mode == 1 ) 353 | style &= ~kRenderWireframeBack; 354 | else 355 | style |= kRenderWireframeBack; 356 | } 357 | else 358 | style &= ~kRenderWireframe; 359 | 360 | Scene3DNode_setRenderStyle(node, style); 361 | 362 | return 0; 363 | } 364 | 365 | static int node_setWireframeColor(lua_State* L) 366 | { 367 | Scene3DNode* node = getSceneNode(1); 368 | int color = pd->lua->getArgInt(2); 369 | 370 | RenderStyle style = Scene3DNode_getRenderStyle(node); 371 | 372 | if ( color ) 373 | style |= kRenderWireframeWhite; 374 | else 375 | style &= ~kRenderWireframeWhite; 376 | 377 | Scene3DNode_setRenderStyle(node, style); 378 | 379 | return 0; 380 | } 381 | 382 | static int node_setVisible(lua_State* L) 383 | { 384 | Scene3DNode* node = getSceneNode(1); 385 | 386 | Scene3DNode_setVisible(node, pd->lua->getArgBool(2)); 387 | 388 | return 0; 389 | } 390 | 391 | static const lua_reg lib3DNode[] = 392 | { 393 | { "__gc", node_gc }, 394 | { "addChildNode", node_makeChildNode }, 395 | { "addShape", node_addShape }, 396 | { "addTransform", node_addTransform }, 397 | { "setTransform", node_setTransform }, 398 | { "translateBy", node_translateBy }, 399 | { "scaleBy", node_scaleBy }, 400 | { "setColorBias", node_setColorBias }, 401 | { "setFilled", node_setFilled }, 402 | { "setWireframeMode", node_setWireframeMode }, 403 | { "setWireframeColor", node_setWireframeColor }, 404 | { "setVisible", node_setVisible }, 405 | { NULL, NULL } 406 | }; 407 | 408 | 409 | /// Shape 410 | 411 | static int shape_new(lua_State* L) 412 | { 413 | Shape3D* shape = m3d_malloc(sizeof(Shape3D)); 414 | Shape3D_init(shape); 415 | Shape3D_retain(shape); 416 | pd->lua->pushObject(shape, "lib3d.shape", 0); 417 | return 1; 418 | } 419 | 420 | static int shape_gc(lua_State* L) 421 | { 422 | Shape3D* shape = getShape(1); 423 | Shape3D_release(shape); 424 | return 0; 425 | } 426 | 427 | static int shape_addFace(lua_State* L) 428 | { 429 | Shape3D* shape = getShape(1); 430 | Point3D* a = getPoint(2); 431 | Point3D* b = getPoint(3); 432 | Point3D* c = getPoint(4); 433 | 434 | Point3D* d = NULL; 435 | const char* type; 436 | float color = 0; 437 | 438 | if ( pd->lua->getArgType(5, &type) == kTypeObject && strcmp(type, "lib3d.point") == 0 ) 439 | { 440 | d = getPoint(5); 441 | color = pd->lua->getArgFloat(6); 442 | } 443 | else 444 | color = pd->lua->getArgFloat(5); 445 | 446 | Shape3D_addFace(shape, a, b, c, d, color); 447 | 448 | return 0; 449 | } 450 | 451 | static int shape_setClosed(lua_State* L) 452 | { 453 | Shape3D_setClosed(getShape(1), pd->lua->getArgBool(2)); 454 | return 0; 455 | } 456 | 457 | #if ENABLE_ORDERING_TABLE 458 | static int shape_setOrderTableSize(lua_State* L) 459 | { 460 | Shape3D_setOrderTableSize(getShape(1), pd->lua->getArgInt(2)); 461 | return 0; 462 | } 463 | #endif 464 | 465 | static const lua_reg lib3DShape[] = 466 | { 467 | { "new", shape_new }, 468 | { "__gc", shape_gc }, 469 | { "addFace", shape_addFace }, 470 | { "setClosed", shape_setClosed }, 471 | #if ENABLE_ORDERING_TABLE 472 | { "setOrderTableSize", shape_setOrderTableSize, }, 473 | #endif 474 | { NULL, NULL } 475 | }; 476 | 477 | 478 | /// Point 479 | 480 | static int point_new(lua_State* L) 481 | { 482 | Point3D* p = m3d_malloc(sizeof(Point3D)); 483 | p->x = pd->lua->getArgFloat(1); 484 | p->y = pd->lua->getArgFloat(2); 485 | p->z = pd->lua->getArgFloat(3); 486 | pd->lua->pushObject(p, "lib3d.point", 0); 487 | return 1; 488 | } 489 | 490 | static int point_gc(lua_State* L) 491 | { 492 | Point3D* p = getPoint(1); 493 | m3d_free(p); 494 | return 0; 495 | } 496 | 497 | static int point_index(lua_State* L) 498 | { 499 | Point3D* p = getPoint(1); 500 | const char* arg = pd->lua->getArgString(2); 501 | 502 | if ( strcmp(arg, "x") == 0 ) 503 | pd->lua->pushFloat(p->x); 504 | else if ( strcmp(arg, "y") == 0 ) 505 | pd->lua->pushFloat(p->y); 506 | else if ( strcmp(arg, "z") == 0 ) 507 | pd->lua->pushFloat(p->z); 508 | else 509 | pd->lua->pushNil(); 510 | 511 | return 1; 512 | } 513 | 514 | static int point_newindex(lua_State* L) 515 | { 516 | Point3D* p = getPoint(1); 517 | const char* arg = pd->lua->getArgString(2); 518 | 519 | if ( strcmp(arg, "x") == 0 ) 520 | p->x = pd->lua->getArgFloat(3); 521 | else if ( strcmp(arg, "y") == 0 ) 522 | p->y = pd->lua->getArgFloat(3); 523 | else if ( strcmp(arg, "z") == 0 ) 524 | p->z = pd->lua->getArgFloat(3); 525 | 526 | return 0; 527 | } 528 | 529 | static int point_mul(lua_State* L) 530 | { 531 | Point3D* p = getPoint(1); 532 | Matrix3D* m = getMatrix(2); 533 | 534 | Point3D* p2 = m3d_malloc(sizeof(Point3D)); 535 | *p2 = Matrix3D_apply(*m, *p); 536 | pd->lua->pushObject(p2, "lib3d.point", 0); 537 | return 1; 538 | } 539 | 540 | static const lua_reg lib3DPoint[] = 541 | { 542 | { "new", point_new }, 543 | { "__gc", point_gc }, 544 | { "__index", point_index }, 545 | { "__newindex", point_newindex }, 546 | { "__mul", point_mul }, 547 | { NULL, NULL } 548 | }; 549 | 550 | 551 | /// Matrix 552 | 553 | static int matrix_gc(lua_State* L) 554 | { 555 | Matrix3D* m = getMatrix(1); 556 | m3d_free(m); 557 | return 0; 558 | } 559 | 560 | static int matrix_mul(lua_State* L) 561 | { 562 | Matrix3D* l = getMatrix(1); 563 | Matrix3D* r = getMatrix(2); 564 | Matrix3D* m = m3d_malloc(sizeof(Matrix3D)); 565 | 566 | *m = Matrix3D_multiply(*l, *r); 567 | 568 | pd->lua->pushObject(m, "lib3d.matrix", 0); 569 | return 1; 570 | } 571 | 572 | static int matrix_new(lua_State* L) 573 | { 574 | Matrix3D* p = m3d_malloc(sizeof(Matrix3D)); 575 | 576 | p->isIdentity = 0; 577 | p->m[0][0] = pd->lua->getArgFloat(1); 578 | p->m[0][1] = pd->lua->getArgFloat(2); 579 | p->m[0][2] = pd->lua->getArgFloat(3); 580 | p->m[1][0] = pd->lua->getArgFloat(4); 581 | p->m[1][1] = pd->lua->getArgFloat(5); 582 | p->m[1][2] = pd->lua->getArgFloat(6); 583 | p->m[2][0] = pd->lua->getArgFloat(7); 584 | p->m[2][1] = pd->lua->getArgFloat(8); 585 | p->m[2][2] = pd->lua->getArgFloat(9); 586 | p->dx = pd->lua->getArgFloat(10); 587 | p->dy = pd->lua->getArgFloat(11); 588 | p->dz = pd->lua->getArgFloat(12); 589 | 590 | p->inverting = Matrix3D_getDeterminant(p) < 0; 591 | 592 | pd->lua->pushObject(p, "lib3d.matrix", 0); 593 | return 1; 594 | } 595 | 596 | static int matrix_newRotation(lua_State* L) 597 | { 598 | Matrix3D* p = m3d_malloc(sizeof(Matrix3D)); 599 | 600 | #undef M_PI 601 | #define M_PI 3.14159265358979323846f 602 | 603 | float angle = pd->lua->getArgFloat(1); 604 | float c = cosf(angle * M_PI / 180); 605 | float s = sinf(angle * M_PI / 180); 606 | float x = pd->lua->getArgFloat(2); 607 | float y = pd->lua->getArgFloat(3); 608 | float z = pd->lua->getArgFloat(4); 609 | 610 | float d = sqrtf(x * x + y * y + z * z); 611 | 612 | x /= d; 613 | y /= d; 614 | z /= d; 615 | 616 | p->isIdentity = 0; 617 | p->inverting = 0; 618 | 619 | p->m[0][0] = c + x * x * (1-c); 620 | p->m[0][1] = x * y * (1-c) - z * s; 621 | p->m[0][2] = x * z * (1-c) + y * s; 622 | p->m[1][0] = y * x * (1-c) + z * s; 623 | p->m[1][1] = c + y * y * (1-c); 624 | p->m[1][2] = y * z * (1-c) - x * s; 625 | p->m[2][0] = z * x * (1-c) - y * s; 626 | p->m[2][1] = z * y * (1-c) + x * s; 627 | p->m[2][2] = c + z * z * (1-c); 628 | p->dx = p->dy = p->dz = 0; 629 | 630 | pd->lua->pushObject(p, "lib3d.matrix", 0); 631 | return 1; 632 | } 633 | 634 | static int matrix_addTranslation(lua_State* L) 635 | { 636 | Matrix3D* l = getMatrix(1); 637 | l->dx += pd->lua->getArgFloat(2); 638 | l->dy += pd->lua->getArgFloat(3); 639 | l->dz += pd->lua->getArgFloat(4); 640 | return 0; 641 | } 642 | 643 | static const lua_reg lib3DMatrix[] = 644 | { 645 | { "__gc", matrix_gc }, 646 | { "__mul", matrix_mul }, 647 | { "new", matrix_new }, 648 | { "newRotation", matrix_newRotation }, 649 | { "addTranslation", matrix_addTranslation }, 650 | { NULL, NULL } 651 | }; 652 | 653 | static int setPalette(lua_State* L) 654 | { 655 | uint8_t palette[3*16]; 656 | 657 | float hue = 6 * pd->lua->getArgFloat(1); 658 | float d = hue - floorf(hue); 659 | int n = (unsigned int)floorf(hue) % 6; 660 | 661 | float r, g, b; 662 | 663 | switch ( n ) 664 | { 665 | case 0: r=1; g=d; b=0; break; 666 | case 1: r=1-d; g=1; b=0; break; 667 | case 2: r=0; g=1; b=d; break; 668 | case 3: r=0; g=1-d; b=1; break; 669 | case 4: r=d; g=0; b=1; break; 670 | case 5: r=1; g=0; b=1-d; break; 671 | } 672 | 673 | for ( int i = 0; i < 16; ++i ) 674 | { 675 | float l = 255.0f * i / 15; 676 | float w = i > 4 ? (i-4)/11.0f : 0; 677 | 678 | palette[3*i+0] = l * (w + (1-w)*r); 679 | palette[3*i+1] = l * (w + (1-w)*g); 680 | palette[3*i+2] = l * (w + (1-w)*b); 681 | } 682 | 683 | pd->system->sendMirrorData(2, palette, sizeof(palette)); 684 | return 0; 685 | } 686 | 687 | static int resetFrame(lua_State* L) 688 | { 689 | pd->system->sendMirrorData(0, NULL, 0); 690 | return 0; 691 | } 692 | 693 | void register3D(PlaydateAPI* playdate) 694 | { 695 | pd = playdate; 696 | 697 | const char* err; 698 | 699 | if (!pd->lua->registerClass("lib3d.scene", lib3DScene, NULL, 0, &err)) 700 | pd->system->logToConsole("%s:%i: registerClass failed, %s", __FILE__, __LINE__, err); 701 | 702 | if (!pd->lua->registerClass("lib3d.scenenode", lib3DNode, NULL, 0, &err)) 703 | pd->system->logToConsole("%s:%i: registerClass failed, %s", __FILE__, __LINE__, err); 704 | 705 | if (!pd->lua->registerClass("lib3d.shape", lib3DShape, NULL, 0, &err)) 706 | pd->system->logToConsole("%s:%i: registerClass failed, %s", __FILE__, __LINE__, err); 707 | 708 | if (!pd->lua->registerClass("lib3d.point", lib3DPoint, NULL, 0, &err)) 709 | pd->system->logToConsole("%s:%i: registerClass failed, %s", __FILE__, __LINE__, err); 710 | 711 | if (!pd->lua->registerClass("lib3d.matrix", lib3DMatrix, NULL, 0, &err)) 712 | pd->system->logToConsole("%s:%i: registerClass failed, %s", __FILE__, __LINE__, err); 713 | 714 | if (!pd->lua->addFunction(setPalette, "setPalette", &err) || !pd->lua->addFunction(resetFrame, "resetFrame", &err)) 715 | pd->system->logToConsole("%s:%i: addFunction failed, %s", __FILE__, __LINE__, err); 716 | 717 | mini3d_setRealloc(pd->system->realloc); 718 | } 719 | -------------------------------------------------------------------------------- /colorknot/mini3d/scene.c: -------------------------------------------------------------------------------- 1 | // 2 | // scene.c 3 | // Extension 4 | // 5 | // Created by Dave Hayden on 10/7/15. 6 | // Copyright © 2015 Panic, Inc. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include "3dmath.h" 12 | #include "mini3d.h" 13 | #include "scene.h" 14 | #include "shape.h" 15 | #include "render.h" 16 | 17 | #define WIDTH 200 18 | #define HEIGHT 120 19 | 20 | void 21 | Scene3DNode_init(Scene3DNode* node) 22 | { 23 | node->transform = identityMatrix; 24 | node->parentNode = NULL; 25 | node->childNodes = NULL; 26 | node->nChildren = 0; 27 | node->shapes = NULL; 28 | node->colorBias = 0; 29 | node->renderStyle = kRenderInheritStyle; 30 | node->isVisible = 1; 31 | node->needsUpdate = 1; 32 | #if ENABLE_Z_BUFFER 33 | node->useZBuffer = 1; 34 | #endif 35 | } 36 | 37 | void 38 | Scene3DNode_deinit(Scene3DNode* node) 39 | { 40 | ShapeInstance* shape = node->shapes; 41 | 42 | while ( shape != NULL ) 43 | { 44 | ShapeInstance* next = shape->next; 45 | Shape3D_release(shape->prototype); 46 | m3d_free(shape->points); 47 | m3d_free(shape->faces); 48 | m3d_free(shape); 49 | shape = next; 50 | } 51 | 52 | if ( node->shapes != NULL ) 53 | m3d_free(node->shapes); 54 | 55 | int i; 56 | 57 | for ( i = 0; i < node->nChildren; ++i ) 58 | { 59 | Scene3DNode_deinit(node->childNodes[i]); 60 | m3d_free(node->childNodes[i]); 61 | } 62 | 63 | if ( node->childNodes != NULL ) 64 | m3d_free(node->childNodes); 65 | 66 | node->childNodes = NULL; 67 | } 68 | 69 | void 70 | Scene3DNode_setTransform(Scene3DNode* node, Matrix3D* xform) 71 | { 72 | node->transform = *xform; 73 | 74 | // mark this branch of the tree for updating 75 | 76 | while ( node != NULL ) 77 | { 78 | node->needsUpdate = 1; 79 | node = node->parentNode; 80 | } 81 | } 82 | 83 | void 84 | Scene3DNode_addTransform(Scene3DNode* node, Matrix3D* xform) 85 | { 86 | Matrix3D m = Matrix3D_multiply(node->transform, *xform); 87 | Scene3DNode_setTransform(node, &m); 88 | } 89 | 90 | void 91 | Scene3DNode_setColorBias(Scene3DNode* node, float bias) 92 | { 93 | node->colorBias = bias; 94 | node->needsUpdate = 1; 95 | } 96 | 97 | void 98 | Scene3DNode_setRenderStyle(Scene3DNode* node, RenderStyle style) 99 | { 100 | node->renderStyle = style; 101 | node->needsUpdate = 1; 102 | } 103 | 104 | RenderStyle 105 | Scene3DNode_getRenderStyle(Scene3DNode* node) 106 | { 107 | while ( node != NULL ) 108 | { 109 | if ( node->renderStyle != kRenderInheritStyle ) 110 | return node->renderStyle; 111 | 112 | node = node->parentNode; 113 | } 114 | 115 | return kRenderFilled; 116 | } 117 | 118 | void 119 | Scene3DNode_setVisible(Scene3DNode* node, int visible) 120 | { 121 | node->isVisible = visible; 122 | node->needsUpdate = 1; 123 | } 124 | 125 | #if ENABLE_Z_BUFFER 126 | void Scene3DNode_setUsesZBuffer(Scene3DNode* node, int flag) 127 | { 128 | node->useZBuffer = flag; 129 | node->needsUpdate = 1; 130 | } 131 | #endif 132 | 133 | void 134 | Scene3DNode_addShapeWithTransform(Scene3DNode* node, Shape3D* shape, Matrix3D transform) 135 | { 136 | ShapeInstance* nodeshape = m3d_malloc(sizeof(ShapeInstance)); 137 | int i; 138 | 139 | nodeshape->renderStyle = kRenderInheritStyle; 140 | nodeshape->prototype = Shape3D_retain(shape); 141 | nodeshape->nPoints = shape->nPoints; 142 | nodeshape->points = m3d_malloc(sizeof(Point3D) * shape->nPoints); 143 | 144 | // not strictly necessary, since we set these again in Scene3D_updateShapeInstance: 145 | //for ( i = 0; i < shape->nPoints; ++i ) 146 | // nodeshape->points[i] = shape->points[i]; 147 | 148 | nodeshape->nFaces = shape->nFaces; 149 | nodeshape->faces = m3d_malloc(sizeof(FaceInstance) * shape->nFaces); 150 | 151 | for ( i = 0; i < shape->nFaces; ++i ) 152 | { 153 | // point face vertices at copy's points array 154 | FaceInstance* face = &nodeshape->faces[i]; 155 | 156 | face->p1 = &nodeshape->points[shape->faces[i].p1]; 157 | face->p2 = &nodeshape->points[shape->faces[i].p2]; 158 | face->p3 = &nodeshape->points[shape->faces[i].p3]; 159 | 160 | if ( shape->faces[i].p4 != 0xffff ) 161 | face->p4 = &nodeshape->points[shape->faces[i].p4]; 162 | else 163 | face->p4 = NULL; 164 | 165 | face->colorBias = shape->faces[i].colorBias; 166 | 167 | // also not necessary 168 | face->normal = pnormal(face->p1, face->p2, face->p3); 169 | } 170 | 171 | nodeshape->transform = transform; 172 | nodeshape->center = Matrix3D_apply(transform, shape->center); 173 | nodeshape->colorBias = 0; 174 | 175 | #if ENABLE_ORDERING_TABLE 176 | nodeshape->orderTableSize = 0; 177 | nodeshape->orderTable = NULL; 178 | #endif 179 | 180 | nodeshape->next = node->shapes; 181 | node->shapes = nodeshape; 182 | ++node->shapeCount; 183 | } 184 | 185 | void 186 | Scene3DNode_addShapeWithOffset(Scene3DNode* node, Shape3D* shape, Vector3D offset) 187 | { 188 | Scene3DNode_addShapeWithTransform(node, shape, Matrix3DMakeTranslate(offset.dx, offset.dy, offset.dz)); 189 | } 190 | 191 | void 192 | Scene3DNode_addShape(Scene3DNode* node, Shape3D* shape) 193 | { 194 | Scene3DNode_addShapeWithTransform(node, shape, identityMatrix); 195 | } 196 | 197 | Scene3DNode* 198 | Scene3DNode_newChild(Scene3DNode* node) 199 | { 200 | node->childNodes = m3d_realloc(node->childNodes, sizeof(Scene3DNode*) * (node->nChildren + 1)); 201 | 202 | Scene3DNode* child = m3d_malloc(sizeof(Scene3DNode)); 203 | Scene3DNode_init(child); 204 | 205 | node->childNodes[node->nChildren++] = child; 206 | child->parentNode = node; 207 | 208 | return child; 209 | } 210 | 211 | static void 212 | Scene3D_updateShapeInstance(Scene3D* scene, ShapeInstance* shape, Matrix3D xform, float colorBias, RenderStyle style) 213 | { 214 | Shape3D* proto = shape->prototype; 215 | int i; 216 | 217 | // transform points 218 | 219 | for ( i = 0; i < shape->nPoints; ++i ) 220 | shape->points[i] = Matrix3D_apply(xform, Matrix3D_apply(shape->transform, proto->points[i])); 221 | 222 | shape->center = Matrix3D_apply(xform, Matrix3D_apply(shape->transform, proto->center)); 223 | shape->colorBias = proto->colorBias + colorBias; 224 | shape->renderStyle = style; 225 | shape->inverted = xform.inverting; 226 | 227 | #if ENABLE_ORDERING_TABLE 228 | float zmin = 1e23f; 229 | float zmax = 0; 230 | int ordersize = shape->prototype->orderTableSize; 231 | 232 | if ( ordersize != shape->orderTableSize ) 233 | { 234 | shape->orderTableSize = ordersize; 235 | shape->orderTable = m3d_realloc(shape->orderTable, ordersize * sizeof(FaceInstance*)); 236 | } 237 | 238 | memset(shape->orderTable, 0, ordersize * sizeof(FaceInstance*)); 239 | #endif 240 | 241 | // recompute face normals 242 | 243 | for ( i = 0; i < shape->nFaces; ++i ) 244 | { 245 | FaceInstance* face = &shape->faces[i]; 246 | face->normal = pnormal(face->p1, face->p2, face->p3); 247 | 248 | #if ENABLE_ORDERING_TABLE 249 | if ( ordersize > 0 ) 250 | { 251 | float z = face->p1->z + face->p2->z + face->p3->z; 252 | 253 | if ( z < zmin ) zmin = z; 254 | if ( z > zmax ) zmax = z; 255 | } 256 | #endif 257 | } 258 | 259 | // apply perspective, scale to display 260 | 261 | for ( i = 0; i < shape->nPoints; ++i ) 262 | { 263 | Point3D* p = &shape->points[i]; 264 | 265 | if ( p->z > 0 ) 266 | { 267 | if ( scene->hasPerspective ) 268 | { 269 | p->x = scene->scale * (p->x / p->z + 1.6666666f * scene->centerx); 270 | p->y = scene->scale * (p->y / p->z + scene->centery); 271 | } 272 | else 273 | { 274 | p->x = scene->scale * (p->x + 1.6666666f * scene->centerx); 275 | p->y = scene->scale * (p->y + scene->centery); 276 | } 277 | } 278 | 279 | #if ENABLE_Z_BUFFER 280 | if ( p->z < scene->zmin ) 281 | scene->zmin = p->z; 282 | #endif 283 | } 284 | 285 | #if ENABLE_ORDERING_TABLE 286 | // Put faces in bins separated by average z value 287 | 288 | if ( ordersize > 0 ) 289 | { 290 | float d = zmax - zmin + 0.0001f; 291 | 292 | for ( i = 0; i < shape->nFaces; ++i ) 293 | { 294 | FaceInstance* face = &shape->faces[i]; 295 | 296 | int idx = ordersize * (face->p1->z + face->p2->z + face->p3->z - zmin) / d; 297 | face->next = shape->orderTable[idx]; 298 | shape->orderTable[idx] = face; 299 | } 300 | } 301 | #endif 302 | } 303 | 304 | void 305 | Scene3D_updateNode(Scene3D* scene, Scene3DNode* node, Matrix3D xform, float colorBias, RenderStyle style, int update) 306 | { 307 | if ( !node->isVisible ) 308 | return; 309 | 310 | if ( node->needsUpdate ) 311 | { 312 | update = 1; 313 | node->needsUpdate = 0; 314 | } 315 | 316 | if ( update ) 317 | { 318 | xform = Matrix3D_multiply(node->transform, xform); 319 | colorBias += node->colorBias; 320 | 321 | if ( node->renderStyle != kRenderInheritStyle ) 322 | style = node->renderStyle; 323 | 324 | ShapeInstance* shape = node->shapes; 325 | 326 | while ( shape != NULL ) 327 | { 328 | Scene3D_updateShapeInstance(scene, shape, xform, colorBias, style); 329 | #if ENABLE_Z_BUFFER 330 | shape->useZBuffer = node->useZBuffer; 331 | #endif 332 | shape = shape->next; 333 | } 334 | 335 | int i; 336 | 337 | for ( i = 0; i < node->nChildren; ++i ) 338 | Scene3D_updateNode(scene, node->childNodes[i], xform, colorBias, style, update); 339 | } 340 | } 341 | 342 | 343 | void 344 | Scene3D_init(Scene3D* scene) 345 | { 346 | scene->hasPerspective = 1; 347 | 348 | Scene3D_setCamera(scene, (Point3D){ 0, 0, 0 }, (Point3D){ 0, 0, 1 }, 1.0, (Vector3D){ 0, 1, 0 }); 349 | Scene3D_setGlobalLight(scene, (Vector3D){ 0, -1, 0 }); 350 | 351 | Scene3D_setCenter(scene, 0.5, 0.5); 352 | 353 | Scene3DNode_init(&scene->root); 354 | 355 | scene->shapelist = NULL; 356 | scene->shapelistsize = 0; 357 | } 358 | 359 | void 360 | Scene3D_deinit(Scene3D* scene) 361 | { 362 | if ( scene->shapelist != NULL ) 363 | m3d_free(scene->shapelist); 364 | 365 | Scene3DNode_deinit(&scene->root); 366 | } 367 | 368 | void 369 | Scene3D_setGlobalLight(Scene3D* scene, Vector3D light) 370 | { 371 | scene->light = light; 372 | } 373 | 374 | void 375 | Scene3D_setCenter(Scene3D* scene, float x, float y) 376 | { 377 | scene->centerx = x; 378 | scene->centery = y; 379 | } 380 | 381 | Scene3DNode* 382 | Scene3D_getRootNode(Scene3D* scene) 383 | { 384 | return &scene->root; 385 | } 386 | 387 | static int 388 | getShapesAtNode(Scene3D* scene, Scene3DNode* node, int count) 389 | { 390 | if ( !node->isVisible ) 391 | return count; 392 | 393 | ShapeInstance* shape = node->shapes; 394 | ShapeInstance** shapes = scene->shapelist; 395 | 396 | while ( shape != NULL ) 397 | { 398 | // check if shape is outside camera view: apply camera transform to shape center 399 | 400 | if ( count + 1 > scene->shapelistsize ) 401 | { 402 | #define SHAPELIST_INCREMENT 16 403 | scene->shapelist = m3d_realloc(scene->shapelist, (scene->shapelistsize + SHAPELIST_INCREMENT) * sizeof(ShapeInstance*)); 404 | shapes = scene->shapelist; 405 | 406 | scene->shapelistsize += SHAPELIST_INCREMENT; 407 | } 408 | 409 | shapes[count++] = shape; 410 | shape = shape->next; 411 | } 412 | 413 | int i; 414 | 415 | for ( i = 0; i < node->nChildren; ++i ) 416 | count = getShapesAtNode(scene, node->childNodes[i], count); 417 | 418 | return count; 419 | } 420 | 421 | void 422 | Scene3D_setCamera(Scene3D* scene, Point3D origin, Point3D lookAt, float scale, Vector3D up) 423 | { 424 | Matrix3D camera = identityMatrix; 425 | camera.isIdentity = 0; 426 | 427 | camera.dx = -origin.x; 428 | camera.dy = -origin.y; 429 | camera.dz = -origin.z; 430 | 431 | Vector3D dir = Vector3DMake(lookAt.x - origin.x, lookAt.y - origin.y, lookAt.z - origin.z); 432 | 433 | float l = sqrtf(Vector3D_lengthSquared(&dir)); 434 | 435 | dir.dx /= l; 436 | dir.dy /= l; 437 | dir.dz /= l; 438 | 439 | scene->scale = HEIGHT * scale; 440 | 441 | // first yaw around the y axis 442 | 443 | float h = 0; 444 | 445 | if ( dir.dx != 0 || dir.dz != 0 ) 446 | { 447 | h = sqrtf(dir.dx * dir.dx + dir.dz * dir.dz); 448 | 449 | Matrix3D yaw = Matrix3DMake(dir.dz/h, 0, -dir.dx/h, 0, 1, 0, dir.dx/h, 0, dir.dz/h, 0); 450 | camera = Matrix3D_multiply(camera, yaw); 451 | } 452 | 453 | // then pitch up/down to y elevation 454 | 455 | Matrix3D pitch = Matrix3DMake(1, 0, 0, 0, h, -dir.dy, 0, dir.dy, h, 0); 456 | camera = Matrix3D_multiply(camera, pitch); 457 | 458 | // and roll to position the up vector 459 | 460 | if ( up.dx != 0 || up.dy != 0 ) 461 | { 462 | l = sqrtf(up.dx * up.dx + up.dy * up.dy); 463 | Matrix3D roll = Matrix3DMake(up.dy/l, up.dx/l, 0, -up.dx/l, up.dy/l, 0, 0, 0, 1, 0); 464 | 465 | scene->camera = Matrix3D_multiply(camera, roll); 466 | } 467 | else 468 | scene->camera = camera; 469 | 470 | scene->root.needsUpdate = 1; 471 | } 472 | 473 | static inline void 474 | drawShapeFace(Scene3D* scene, ShapeInstance* shape, uint8_t* bitmap, int rowstride, FaceInstance* face) 475 | { 476 | // If any vertex is behind the camera, skip it 477 | 478 | if ( face->p1->z <= 0 || face->p2->z <= 0 || face->p3->z <= 0 ) 479 | return; 480 | 481 | float x1 = face->p1->x; 482 | float y1 = face->p1->y; 483 | float x2 = face->p2->x; 484 | float y2 = face->p2->y; 485 | float x3 = face->p3->x; 486 | float y3 = face->p3->y; 487 | 488 | // quick bounds check 489 | 490 | if ( (x1 < 0 && x2 < 0 && x3 < 0 && (face->p4 == NULL || face->p4->x < 0)) || 491 | (x1 >= WIDTH && x2 >= WIDTH && x3 >= WIDTH && (face->p4 == NULL || face->p4->x >= WIDTH)) || 492 | (y1 < 0 && y2 < 0 && y3 < 0 && (face->p4 == NULL || face->p4->y < 0)) || 493 | (y1 >= HEIGHT && y2 >= HEIGHT && y3 >= HEIGHT && (face->p4 == NULL || face->p4->y >= HEIGHT)) ) 494 | return; 495 | 496 | if ( shape->prototype->isClosed ) 497 | { 498 | // only render front side of faces 499 | 500 | float d; 501 | 502 | if ( scene->hasPerspective ) // use winding order 503 | d = (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1); 504 | else // use direction of normal 505 | d = face->normal.dz; 506 | 507 | if ( (d >= 0) ^ (shape->inverted ? 1 : 0) ) 508 | return; 509 | } 510 | 511 | // lighting 512 | 513 | float c = face->colorBias + shape->colorBias; 514 | float v; 515 | 516 | if ( c <= -1 ) 517 | v = 0; 518 | else if ( c >= 1 ) 519 | v = 1; 520 | else 521 | { 522 | if ( shape->inverted ) 523 | v = (1.0f + Vector3DDot(face->normal, scene->light)) / 2; 524 | else 525 | v = (1.0f - Vector3DDot(face->normal, scene->light)) / 2; 526 | 527 | if ( c > 0 ) 528 | v = c + (1-c) * v; // map [0,1] to [c,1] 529 | else if ( c < 0 ) 530 | v *= 1 + c; // map [0,1] to [0, 1+c] 531 | } 532 | 533 | int vi = (int)(16 * v); 534 | 535 | if ( vi > 15 ) 536 | vi = 15; 537 | else if ( vi < 0 ) 538 | vi = 0; 539 | 540 | if ( face->p4 != NULL ) 541 | { 542 | #if ENABLE_Z_BUFFER 543 | if ( shape->useZBuffer ) 544 | fillQuad_zbuf(bitmap, rowstride, face->p1, face->p2, face->p3, face->p4, (uint8_t)vi); 545 | else 546 | #endif 547 | fillQuad(bitmap, rowstride, face->p1, face->p2, face->p3, face->p4, (uint8_t)vi); 548 | } 549 | else 550 | { 551 | #if ENABLE_Z_BUFFER 552 | if ( shape->useZBuffer ) 553 | fillTriangle_zbuf(bitmap, rowstride, face->p1, face->p2, face->p3, (uint8_t)vi); 554 | else 555 | #endif 556 | fillTriangle(bitmap, rowstride, face->p1, face->p2, face->p3, (uint8_t)vi); 557 | } 558 | } 559 | static inline void 560 | drawFilledShape(Scene3D* scene, ShapeInstance* shape, uint8_t* bitmap, int rowstride) 561 | { 562 | #if ENABLE_ORDERING_TABLE 563 | if ( shape->orderTableSize > 0 ) 564 | { 565 | for ( int i = shape->orderTableSize - 1; i >= 0; --i ) 566 | { 567 | FaceInstance* face = shape->orderTable[i]; 568 | 569 | while ( face != NULL ) 570 | { 571 | drawShapeFace(scene, shape, bitmap, rowstride, face); 572 | face = face->next; 573 | } 574 | } 575 | } 576 | else 577 | #endif 578 | for ( int f = 0; f < shape->nFaces; ++f ) 579 | drawShapeFace(scene, shape, bitmap, rowstride, &shape->faces[f]); 580 | } 581 | 582 | static inline void 583 | drawWireframe(Scene3D* scene, ShapeInstance* shape, uint8_t* bitmap, int rowstride) 584 | { 585 | int f; 586 | RenderStyle style = shape->renderStyle; 587 | 588 | for ( f = 0; f < shape->nFaces; ++f ) 589 | { 590 | FaceInstance* face = &shape->faces[f]; 591 | 592 | // If any vertex is behind the camera, skip it 593 | 594 | if ( face->p1->z <= 0 || face->p2->z <= 0 || face->p3->z <= 0 || (face->p4 != NULL && face->p4->x <= 0) ) 595 | continue; 596 | 597 | float x1 = face->p1->x; 598 | float y1 = face->p1->y; 599 | float x2 = face->p2->x; 600 | float y2 = face->p2->y; 601 | float x3 = face->p3->x; 602 | float y3 = face->p3->y; 603 | float x4 = (face->p4 != NULL) ? face->p4->x : 0; 604 | float y4 = (face->p4 != NULL) ? face->p4->y : 0; 605 | 606 | // quick bounds check 607 | 608 | if ( (x1 < 0 && x2 < 0 && x3 < 0 && x4 < 0) || (x1 >= WIDTH && x2 >= WIDTH && x3 >= WIDTH && x4 >= WIDTH) || 609 | (y1 < 0 && y2 < 0 && y3 < 0 && x4 < 0) || (y1 >= HEIGHT && y2 >= HEIGHT && y3 >= HEIGHT && y4 >= HEIGHT) ) 610 | continue; 611 | 612 | if ( (style & kRenderWireframeBack) == 0 ) 613 | { 614 | // only render front side of faces 615 | 616 | float d; 617 | 618 | if ( scene->hasPerspective ) // use winding order 619 | d = (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1); 620 | else // use direction of normal 621 | d = face->normal.dz; 622 | 623 | if ( (d > 0) ^ (shape->inverted ? 1 : 0) ) 624 | continue; 625 | } 626 | 627 | // XXX - can avoid 1/2 the drawing if we're doing the entire shape (kRenderWireframeBack is set) 628 | // and the shape is closed: skip lines where y1 > y2 629 | 630 | #if ENABLE_Z_BUFFER 631 | if ( shape->useZBuffer ) 632 | { 633 | drawLine_zbuf(bitmap, rowstride, face->p1, face->p2, 1, 15); 634 | drawLine_zbuf(bitmap, rowstride, face->p2, face->p3, 1, 15); 635 | 636 | if ( face->p4 != NULL ) 637 | { 638 | drawLine_zbuf(bitmap, rowstride, face->p3, face->p4, 1, 15); 639 | drawLine_zbuf(bitmap, rowstride, face->p4, face->p1, 1, 15); 640 | } 641 | else 642 | drawLine_zbuf(bitmap, rowstride, face->p3, face->p1, 1, 15); 643 | } 644 | else 645 | { 646 | #endif 647 | drawLine(bitmap, rowstride, face->p1, face->p2, 1, 15); 648 | drawLine(bitmap, rowstride, face->p2, face->p3, 1, 15); 649 | 650 | if ( face->p4 != NULL ) 651 | { 652 | drawLine(bitmap, rowstride, face->p3, face->p4, 1, 15); 653 | drawLine(bitmap, rowstride, face->p4, face->p1, 1, 15); 654 | } 655 | else 656 | drawLine(bitmap, rowstride, face->p3, face->p1, 1, 15); 657 | #if ENABLE_Z_BUFFER 658 | } 659 | #endif 660 | } 661 | } 662 | 663 | static int compareZ(const void* a, const void* b) 664 | { 665 | ShapeInstance* shapea = *(ShapeInstance**)a; 666 | ShapeInstance* shapeb = *(ShapeInstance**)b; 667 | 668 | return shapea->center.z < shapeb->center.z; 669 | } 670 | 671 | void 672 | Scene3D_drawNode(Scene3D* scene, Scene3DNode* node, uint8_t* bitmap, int rowstride) 673 | { 674 | // order shapes by z 675 | 676 | int count = getShapesAtNode(scene, node, 0); 677 | 678 | // and draw back to front 679 | 680 | if ( count > 1 ) 681 | qsort(scene->shapelist, count, sizeof(ShapeInstance*), compareZ); 682 | 683 | int i; 684 | 685 | for ( i = 0; i < count; ++i ) 686 | { 687 | ShapeInstance* shape = scene->shapelist[i]; 688 | RenderStyle style = shape->renderStyle; 689 | 690 | if ( style & kRenderFilled ) 691 | drawFilledShape(scene, shape, bitmap, rowstride); 692 | 693 | if ( style & kRenderWireframe ) 694 | drawWireframe(scene, shape, bitmap, rowstride); 695 | } 696 | } 697 | 698 | void 699 | Scene3D_draw(Scene3D* scene, uint8_t* bitmap, int rowstride) 700 | { 701 | #if ENABLE_Z_BUFFER 702 | scene->zmin = 1e23f; 703 | #endif 704 | 705 | Scene3D_updateNode(scene, &scene->root, scene->camera, 0, kRenderFilled, 0); 706 | 707 | #if ENABLE_Z_BUFFER 708 | resetZBuffer(scene->zmin); 709 | #endif 710 | 711 | Scene3D_drawNode(scene, &scene->root, bitmap, rowstride); 712 | } 713 | -------------------------------------------------------------------------------- /colorknot/core_cmFunc.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************//** 2 | * @file core_cmFunc.h 3 | * @brief CMSIS Cortex-M Core Function Access Header File 4 | * @version V4.00 5 | * @date 28. August 2014 6 | * 7 | * @note 8 | * 9 | ******************************************************************************/ 10 | /* Copyright (c) 2009 - 2014 ARM LIMITED 11 | 12 | All rights reserved. 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | - Redistributions of source code must retain the above copyright 16 | notice, this list of conditions and the following disclaimer. 17 | - Redistributions in binary form must reproduce the above copyright 18 | notice, this list of conditions and the following disclaimer in the 19 | documentation and/or other materials provided with the distribution. 20 | - Neither the name of ARM nor the names of its contributors may be used 21 | to endorse or promote products derived from this software without 22 | specific prior written permission. 23 | * 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 | ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE 28 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 | POSSIBILITY OF SUCH DAMAGE. 35 | ---------------------------------------------------------------------------*/ 36 | 37 | 38 | #ifndef __CORE_CMFUNC_H 39 | #define __CORE_CMFUNC_H 40 | 41 | 42 | /* ########################### Core Function Access ########################### */ 43 | /** \ingroup CMSIS_Core_FunctionInterface 44 | \defgroup CMSIS_Core_RegAccFunctions CMSIS Core Register Access Functions 45 | @{ 46 | */ 47 | 48 | #if defined ( __CC_ARM ) /*------------------RealView Compiler -----------------*/ 49 | /* ARM armcc specific functions */ 50 | 51 | #if (__ARMCC_VERSION < 400677) 52 | #error "Please use ARM Compiler Toolchain V4.0.677 or later!" 53 | #endif 54 | 55 | /* intrinsic void __enable_irq(); */ 56 | /* intrinsic void __disable_irq(); */ 57 | 58 | /** \brief Get Control Register 59 | 60 | This function returns the content of the Control Register. 61 | 62 | \return Control Register value 63 | */ 64 | __STATIC_INLINE uint32_t __get_CONTROL(void) 65 | { 66 | register uint32_t __regControl __ASM("control"); 67 | return(__regControl); 68 | } 69 | 70 | 71 | /** \brief Set Control Register 72 | 73 | This function writes the given value to the Control Register. 74 | 75 | \param [in] control Control Register value to set 76 | */ 77 | __STATIC_INLINE void __set_CONTROL(uint32_t control) 78 | { 79 | register uint32_t __regControl __ASM("control"); 80 | __regControl = control; 81 | } 82 | 83 | 84 | /** \brief Get IPSR Register 85 | 86 | This function returns the content of the IPSR Register. 87 | 88 | \return IPSR Register value 89 | */ 90 | __STATIC_INLINE uint32_t __get_IPSR(void) 91 | { 92 | register uint32_t __regIPSR __ASM("ipsr"); 93 | return(__regIPSR); 94 | } 95 | 96 | 97 | /** \brief Get APSR Register 98 | 99 | This function returns the content of the APSR Register. 100 | 101 | \return APSR Register value 102 | */ 103 | __STATIC_INLINE uint32_t __get_APSR(void) 104 | { 105 | register uint32_t __regAPSR __ASM("apsr"); 106 | return(__regAPSR); 107 | } 108 | 109 | 110 | /** \brief Get xPSR Register 111 | 112 | This function returns the content of the xPSR Register. 113 | 114 | \return xPSR Register value 115 | */ 116 | __STATIC_INLINE uint32_t __get_xPSR(void) 117 | { 118 | register uint32_t __regXPSR __ASM("xpsr"); 119 | return(__regXPSR); 120 | } 121 | 122 | 123 | /** \brief Get Process Stack Pointer 124 | 125 | This function returns the current value of the Process Stack Pointer (PSP). 126 | 127 | \return PSP Register value 128 | */ 129 | __STATIC_INLINE uint32_t __get_PSP(void) 130 | { 131 | register uint32_t __regProcessStackPointer __ASM("psp"); 132 | return(__regProcessStackPointer); 133 | } 134 | 135 | 136 | /** \brief Set Process Stack Pointer 137 | 138 | This function assigns the given value to the Process Stack Pointer (PSP). 139 | 140 | \param [in] topOfProcStack Process Stack Pointer value to set 141 | */ 142 | __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) 143 | { 144 | register uint32_t __regProcessStackPointer __ASM("psp"); 145 | __regProcessStackPointer = topOfProcStack; 146 | } 147 | 148 | 149 | /** \brief Get Main Stack Pointer 150 | 151 | This function returns the current value of the Main Stack Pointer (MSP). 152 | 153 | \return MSP Register value 154 | */ 155 | __STATIC_INLINE uint32_t __get_MSP(void) 156 | { 157 | register uint32_t __regMainStackPointer __ASM("msp"); 158 | return(__regMainStackPointer); 159 | } 160 | 161 | 162 | /** \brief Set Main Stack Pointer 163 | 164 | This function assigns the given value to the Main Stack Pointer (MSP). 165 | 166 | \param [in] topOfMainStack Main Stack Pointer value to set 167 | */ 168 | __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) 169 | { 170 | register uint32_t __regMainStackPointer __ASM("msp"); 171 | __regMainStackPointer = topOfMainStack; 172 | } 173 | 174 | 175 | /** \brief Get Priority Mask 176 | 177 | This function returns the current state of the priority mask bit from the Priority Mask Register. 178 | 179 | \return Priority Mask value 180 | */ 181 | __STATIC_INLINE uint32_t __get_PRIMASK(void) 182 | { 183 | register uint32_t __regPriMask __ASM("primask"); 184 | return(__regPriMask); 185 | } 186 | 187 | 188 | /** \brief Set Priority Mask 189 | 190 | This function assigns the given value to the Priority Mask Register. 191 | 192 | \param [in] priMask Priority Mask 193 | */ 194 | __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) 195 | { 196 | register uint32_t __regPriMask __ASM("primask"); 197 | __regPriMask = (priMask); 198 | } 199 | 200 | 201 | #if (__CORTEX_M >= 0x03) || (__CORTEX_SC >= 300) 202 | 203 | /** \brief Enable FIQ 204 | 205 | This function enables FIQ interrupts by clearing the F-bit in the CPSR. 206 | Can only be executed in Privileged modes. 207 | */ 208 | #define __enable_fault_irq __enable_fiq 209 | 210 | 211 | /** \brief Disable FIQ 212 | 213 | This function disables FIQ interrupts by setting the F-bit in the CPSR. 214 | Can only be executed in Privileged modes. 215 | */ 216 | #define __disable_fault_irq __disable_fiq 217 | 218 | 219 | /** \brief Get Base Priority 220 | 221 | This function returns the current value of the Base Priority register. 222 | 223 | \return Base Priority register value 224 | */ 225 | __STATIC_INLINE uint32_t __get_BASEPRI(void) 226 | { 227 | register uint32_t __regBasePri __ASM("basepri"); 228 | return(__regBasePri); 229 | } 230 | 231 | 232 | /** \brief Set Base Priority 233 | 234 | This function assigns the given value to the Base Priority register. 235 | 236 | \param [in] basePri Base Priority value to set 237 | */ 238 | __STATIC_INLINE void __set_BASEPRI(uint32_t basePri) 239 | { 240 | register uint32_t __regBasePri __ASM("basepri"); 241 | __regBasePri = (basePri & 0xff); 242 | } 243 | 244 | 245 | /** \brief Get Fault Mask 246 | 247 | This function returns the current value of the Fault Mask register. 248 | 249 | \return Fault Mask register value 250 | */ 251 | __STATIC_INLINE uint32_t __get_FAULTMASK(void) 252 | { 253 | register uint32_t __regFaultMask __ASM("faultmask"); 254 | return(__regFaultMask); 255 | } 256 | 257 | 258 | /** \brief Set Fault Mask 259 | 260 | This function assigns the given value to the Fault Mask register. 261 | 262 | \param [in] faultMask Fault Mask value to set 263 | */ 264 | __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) 265 | { 266 | register uint32_t __regFaultMask __ASM("faultmask"); 267 | __regFaultMask = (faultMask & (uint32_t)1); 268 | } 269 | 270 | #endif /* (__CORTEX_M >= 0x03) || (__CORTEX_SC >= 300) */ 271 | 272 | 273 | #if (__CORTEX_M == 0x04) || (__CORTEX_M == 0x07) 274 | 275 | /** \brief Get FPSCR 276 | 277 | This function returns the current value of the Floating Point Status/Control register. 278 | 279 | \return Floating Point Status/Control register value 280 | */ 281 | __STATIC_INLINE uint32_t __get_FPSCR(void) 282 | { 283 | #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) 284 | register uint32_t __regfpscr __ASM("fpscr"); 285 | return(__regfpscr); 286 | #else 287 | return(0); 288 | #endif 289 | } 290 | 291 | 292 | /** \brief Set FPSCR 293 | 294 | This function assigns the given value to the Floating Point Status/Control register. 295 | 296 | \param [in] fpscr Floating Point Status/Control value to set 297 | */ 298 | __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) 299 | { 300 | #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) 301 | register uint32_t __regfpscr __ASM("fpscr"); 302 | __regfpscr = (fpscr); 303 | #endif 304 | } 305 | 306 | #endif /* (__CORTEX_M == 0x04) || (__CORTEX_M == 0x07) */ 307 | 308 | 309 | #elif defined ( __GNUC__ ) /*------------------ GNU Compiler ---------------------*/ 310 | /* GNU gcc specific functions */ 311 | 312 | /** \brief Enable IRQ Interrupts 313 | 314 | This function enables IRQ interrupts by clearing the I-bit in the CPSR. 315 | Can only be executed in Privileged modes. 316 | */ 317 | __attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_irq(void) 318 | { 319 | __ASM volatile ("cpsie i" : : : "memory"); 320 | } 321 | 322 | 323 | /** \brief Disable IRQ Interrupts 324 | 325 | This function disables IRQ interrupts by setting the I-bit in the CPSR. 326 | Can only be executed in Privileged modes. 327 | */ 328 | __attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_irq(void) 329 | { 330 | __ASM volatile ("cpsid i" : : : "memory"); 331 | } 332 | 333 | 334 | /** \brief Get Control Register 335 | 336 | This function returns the content of the Control Register. 337 | 338 | \return Control Register value 339 | */ 340 | __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_CONTROL(void) 341 | { 342 | uint32_t result; 343 | 344 | __ASM volatile ("MRS %0, control" : "=r" (result) ); 345 | return(result); 346 | } 347 | 348 | 349 | /** \brief Set Control Register 350 | 351 | This function writes the given value to the Control Register. 352 | 353 | \param [in] control Control Register value to set 354 | */ 355 | __attribute__( ( always_inline ) ) __STATIC_INLINE void __set_CONTROL(uint32_t control) 356 | { 357 | __ASM volatile ("MSR control, %0" : : "r" (control) : "memory"); 358 | } 359 | 360 | 361 | /** \brief Get IPSR Register 362 | 363 | This function returns the content of the IPSR Register. 364 | 365 | \return IPSR Register value 366 | */ 367 | __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_IPSR(void) 368 | { 369 | uint32_t result; 370 | 371 | __ASM volatile ("MRS %0, ipsr" : "=r" (result) ); 372 | return(result); 373 | } 374 | 375 | 376 | /** \brief Get APSR Register 377 | 378 | This function returns the content of the APSR Register. 379 | 380 | \return APSR Register value 381 | */ 382 | __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_APSR(void) 383 | { 384 | uint32_t result; 385 | 386 | __ASM volatile ("MRS %0, apsr" : "=r" (result) ); 387 | return(result); 388 | } 389 | 390 | 391 | /** \brief Get xPSR Register 392 | 393 | This function returns the content of the xPSR Register. 394 | 395 | \return xPSR Register value 396 | */ 397 | __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_xPSR(void) 398 | { 399 | uint32_t result; 400 | 401 | __ASM volatile ("MRS %0, xpsr" : "=r" (result) ); 402 | return(result); 403 | } 404 | 405 | 406 | /** \brief Get Process Stack Pointer 407 | 408 | This function returns the current value of the Process Stack Pointer (PSP). 409 | 410 | \return PSP Register value 411 | */ 412 | __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PSP(void) 413 | { 414 | register uint32_t result; 415 | 416 | __ASM volatile ("MRS %0, psp\n" : "=r" (result) ); 417 | return(result); 418 | } 419 | 420 | 421 | /** \brief Set Process Stack Pointer 422 | 423 | This function assigns the given value to the Process Stack Pointer (PSP). 424 | 425 | \param [in] topOfProcStack Process Stack Pointer value to set 426 | */ 427 | __attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PSP(uint32_t topOfProcStack) 428 | { 429 | __ASM volatile ("MSR psp, %0\n" : : "r" (topOfProcStack) : "sp"); 430 | } 431 | 432 | 433 | /** \brief Get Main Stack Pointer 434 | 435 | This function returns the current value of the Main Stack Pointer (MSP). 436 | 437 | \return MSP Register value 438 | */ 439 | __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_MSP(void) 440 | { 441 | register uint32_t result; 442 | 443 | __ASM volatile ("MRS %0, msp\n" : "=r" (result) ); 444 | return(result); 445 | } 446 | 447 | 448 | /** \brief Set Main Stack Pointer 449 | 450 | This function assigns the given value to the Main Stack Pointer (MSP). 451 | 452 | \param [in] topOfMainStack Main Stack Pointer value to set 453 | */ 454 | __attribute__( ( always_inline ) ) __STATIC_INLINE void __set_MSP(uint32_t topOfMainStack) 455 | { 456 | __ASM volatile ("MSR msp, %0\n" : : "r" (topOfMainStack) : "sp"); 457 | } 458 | 459 | 460 | /** \brief Get Priority Mask 461 | 462 | This function returns the current state of the priority mask bit from the Priority Mask Register. 463 | 464 | \return Priority Mask value 465 | */ 466 | __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_PRIMASK(void) 467 | { 468 | uint32_t result; 469 | 470 | __ASM volatile ("MRS %0, primask" : "=r" (result) ); 471 | return(result); 472 | } 473 | 474 | 475 | /** \brief Set Priority Mask 476 | 477 | This function assigns the given value to the Priority Mask Register. 478 | 479 | \param [in] priMask Priority Mask 480 | */ 481 | __attribute__( ( always_inline ) ) __STATIC_INLINE void __set_PRIMASK(uint32_t priMask) 482 | { 483 | __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); 484 | } 485 | 486 | 487 | #if (__CORTEX_M >= 0x03) 488 | 489 | /** \brief Enable FIQ 490 | 491 | This function enables FIQ interrupts by clearing the F-bit in the CPSR. 492 | Can only be executed in Privileged modes. 493 | */ 494 | __attribute__( ( always_inline ) ) __STATIC_INLINE void __enable_fault_irq(void) 495 | { 496 | __ASM volatile ("cpsie f" : : : "memory"); 497 | } 498 | 499 | 500 | /** \brief Disable FIQ 501 | 502 | This function disables FIQ interrupts by setting the F-bit in the CPSR. 503 | Can only be executed in Privileged modes. 504 | */ 505 | __attribute__( ( always_inline ) ) __STATIC_INLINE void __disable_fault_irq(void) 506 | { 507 | __ASM volatile ("cpsid f" : : : "memory"); 508 | } 509 | 510 | 511 | /** \brief Get Base Priority 512 | 513 | This function returns the current value of the Base Priority register. 514 | 515 | \return Base Priority register value 516 | */ 517 | __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_BASEPRI(void) 518 | { 519 | uint32_t result; 520 | 521 | __ASM volatile ("MRS %0, basepri_max" : "=r" (result) ); 522 | return(result); 523 | } 524 | 525 | 526 | /** \brief Set Base Priority 527 | 528 | This function assigns the given value to the Base Priority register. 529 | 530 | \param [in] basePri Base Priority value to set 531 | */ 532 | __attribute__( ( always_inline ) ) __STATIC_INLINE void __set_BASEPRI(uint32_t value) 533 | { 534 | __ASM volatile ("MSR basepri, %0" : : "r" (value) : "memory"); 535 | } 536 | 537 | 538 | /** \brief Get Fault Mask 539 | 540 | This function returns the current value of the Fault Mask register. 541 | 542 | \return Fault Mask register value 543 | */ 544 | __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FAULTMASK(void) 545 | { 546 | uint32_t result; 547 | 548 | __ASM volatile ("MRS %0, faultmask" : "=r" (result) ); 549 | return(result); 550 | } 551 | 552 | 553 | /** \brief Set Fault Mask 554 | 555 | This function assigns the given value to the Fault Mask register. 556 | 557 | \param [in] faultMask Fault Mask value to set 558 | */ 559 | __attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask) 560 | { 561 | __ASM volatile ("MSR faultmask, %0" : : "r" (faultMask) : "memory"); 562 | } 563 | 564 | #endif /* (__CORTEX_M >= 0x03) */ 565 | 566 | 567 | #if (__CORTEX_M == 0x04) || (__CORTEX_M == 0x07) 568 | 569 | /** \brief Get FPSCR 570 | 571 | This function returns the current value of the Floating Point Status/Control register. 572 | 573 | \return Floating Point Status/Control register value 574 | */ 575 | __attribute__( ( always_inline ) ) __STATIC_INLINE uint32_t __get_FPSCR(void) 576 | { 577 | #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) 578 | uint32_t result; 579 | 580 | /* Empty asm statement works as a scheduling barrier */ 581 | __ASM volatile (""); 582 | __ASM volatile ("VMRS %0, fpscr" : "=r" (result) ); 583 | __ASM volatile (""); 584 | return(result); 585 | #else 586 | return(0); 587 | #endif 588 | } 589 | 590 | 591 | /** \brief Set FPSCR 592 | 593 | This function assigns the given value to the Floating Point Status/Control register. 594 | 595 | \param [in] fpscr Floating Point Status/Control value to set 596 | */ 597 | __attribute__( ( always_inline ) ) __STATIC_INLINE void __set_FPSCR(uint32_t fpscr) 598 | { 599 | #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) 600 | /* Empty asm statement works as a scheduling barrier */ 601 | __ASM volatile (""); 602 | __ASM volatile ("VMSR fpscr, %0" : : "r" (fpscr) : "vfpcc"); 603 | __ASM volatile (""); 604 | #endif 605 | } 606 | 607 | #endif /* (__CORTEX_M == 0x04) || (__CORTEX_M == 0x07) */ 608 | 609 | 610 | #elif defined ( __ICCARM__ ) /*------------------ ICC Compiler -------------------*/ 611 | /* IAR iccarm specific functions */ 612 | #include 613 | 614 | 615 | #elif defined ( __TMS470__ ) /*---------------- TI CCS Compiler ------------------*/ 616 | /* TI CCS specific functions */ 617 | #include 618 | 619 | 620 | #elif defined ( __TASKING__ ) /*------------------ TASKING Compiler --------------*/ 621 | /* TASKING carm specific functions */ 622 | /* 623 | * The CMSIS functions have been implemented as intrinsics in the compiler. 624 | * Please use "carm -?i" to get an up to date list of all intrinsics, 625 | * Including the CMSIS ones. 626 | */ 627 | 628 | 629 | #elif defined ( __CSMC__ ) /*------------------ COSMIC Compiler -------------------*/ 630 | /* Cosmic specific functions */ 631 | #include 632 | 633 | #endif 634 | 635 | /*@} end of CMSIS_Core_RegAccFunctions */ 636 | 637 | #endif /* __CORE_CMFUNC_H */ 638 | --------------------------------------------------------------------------------