[ot][spam][crazy][is it appropriate to direct disruption towards things that have been heavily disrupted?]
hello it is wednesday 908 am here 06-22-2022 or such. i had a rough night. but i worked for like 4 or 5 days straight which was awesome !!!!!! i'm left with my dissociationy stuff with few avenues to pursue with stability that are near me. working memory issues make creative solutions harder, maybe. anyway. quite normal.
let's make a script that abuses the human condition by downloading random AI-generated images i don't expect to succeed, which means i succeed whether I do or not!
i typed "bazz" into https://www.craiyon.com/ , a website i was forwarded to from dall-e mini. i read recently that openai threatened the creator of dall-e mini. i thought dall-e mini was made by a community, not an individual. dunno. i suppose i'm likely wrong. still, it's reminiscent of how military corporations look for influential individuals in communities and describe them as harmful leaders.
i typed in "this is a little nicer" and opened the network dev pane to see if it is a simple api i can just paste into something
it showed me babies and cats with a lot of the color green present (the cat had gree whites to their eyes) i saw a POST request to a 'generate' endpoint. let's see if it's obscure or not
here, i help Boss by loading Dall-E Mini's free public server, sharing these exfiltrated specs. i do this a lot. it's well-known their easy to exfiltrate, but nobody shares like me. Method: POST URL: https://backend.craiyon.com/generate Accept: application/json Content-Type: application/json Body: {"prompt":<prompt string>} Contextual Headers (usually if you need these it indicates you're going to get blocked, if this is a public api likely they aren't needed): Referer: https://www.craiyon.com/ User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36 No cookies. The response has a lot of base64 in it, so I'll plan to open up python to examine it.
On 6/22/22, Undiscussed Horrific Abuse, One Victim of Many <gmkarl@gmail.com> wrote:
here, i help Boss by loading Dall-E Mini's free public server, sharing
I mean that publicising a third-party interface will cost Dall-E Mini money they did not plan to spend. My intention of course is primarily to handle my state of mind, and secondarily to support community solutions to problems, not to burden Dall-E Mini.
import requests generation = requests.post('https://backend.craiyon.com/generate', json=dict(prompt="Waterfall made of cubes")).json
let's see if it works !
i forgot to _call_ the json function
type(generation) <class 'method'> generation = generation() type(generation) <class 'dict'>
then it works.
type(generation) <class 'dict'> generation.keys() dict_keys(['images', 'version']) type(generation['images']) <class 'list'> len(generation['images']) 9 type(generation['images'][0]) <class 'str'>
i can infer it returns 9 base64 encoded things, likely image data, maybe image urls
it looks the response format presently is: { "version": "mega-1:v16", "images": [ "base64-encoded-jpeg-1", "base64-encoded-jpeg-2", ..., "base64-encoded-jpeg-9" ] }
i don't immediately see it mentioned it seems it would be polite to ask at https://huggingface.co/spaces/dalle-mini/dalle-mini/discussions or in the dalle-mini discord i didn't immediately see a way to search the forums for information on this, it can be hard fo rme to see stuff
i did not look at the base64 pictures that may or may not be of a waterfall of cubes. i was daydreaming recently of building a 3D engine that uses ASCII art. maybe i could make a 3D presentation of the stick figure hangman with text, that would run on a terminal. i was thinking it could involve ascii primitives. i wanted to generalise the primitives so that they could be parameterised by their limitations, but i guess it is more normative to limit the entire engine.
i was thinking, how can i make ascii art primitives that can be viewed from any angle? and for some things it's easy. if you restrict the camera so it never rolls (up vector is always the same) then vertical lines (like the post in the hangman) are always vertical, and just change height. totally reasonable. that's inspiring, and that's a normative camera restriction.
similarly, a sphere is incredibly easily to normalise if its distance doesn't change much. it's always an o or an O. the challenge i left off on when i figured out how to move my body, was the idea of doing a rectangular base that's visible as perspective changes. this is pretty reasonable if the camera doesn't rotate around it, always viewing it in an axial manner, but rotations are nice. ideas for rotations include: - making it elliptical instead of rectangular - keeping it axially aligned to the viewer - having it 'jump' - approximating a diagonal view of a rectangle these would probably all work maybe it could instead be point-based. like a little rectangle of periods.
the hard part is using curses, since i focused on text when reinforcing my tech skills
it seems kind of nice to code it in c or c++, since it's ascii and all i'm so much more familiar with python now i guess i'll go with python since the goal is shaky
python curses docs are at https://docs.python.org/3.10/library/curses.html what we will want is: - how to initialise curses - getting the width and height - moving the cursor to a location - blitting an update to the screen blitting might need to be implemented manually, i don't know
oh great, i can blit :D curses.doupdate() Update the physical screen. The curses library keeps two data structures, one representing the current physical screen contents and a virtual screen representing the desired next state. The doupdate() ground updates the physical screen to match the virtual screen. The virtual screen may be updated by a noutrefresh() call after write operations such as addstr() have been performed on a window. The normal refresh() call is simply noutrefresh() followed by doupdate(); if you have to update multiple windows, you can speed performance and perhaps reduce screen flicker by issuing noutrefresh() calls on all windows, followed by a single doupdate(). -- in other news, a mosquito is biting me, which i am super excited about. you need to get bitten by a lot of mosquitoes or you get really sensitive to them and they get almost unmanageably irritating.
it sounds like doupdate is really basic, so i might be able to just copy something from this at the top of the doc page: Curses Programming with Python Tutorial material on using curses with Python, by Andrew Kuchling and Eric Raymond. The Tools/demo/ directory in the Python source distribution contains some example programs using the curses bindings provided by this module.
Here are some pasted together snippets from the tutorial. i don't imagine copypasta to actually work. import curses def main(stdscr): stdscr.nodelay(True) stdscr.clear() while True: try: key = stdscr.getkey() except: key = 'press key?' stdscr.addstr(4, 3, key) stdscr.refresh() curses.wrapper(main)
here's what i have now: import curses def main(stdscr): stdscr.nodelay(True) stdscr.clear() key = 'press key?' while key != 'q': try: key = stdscr.getkey() except: pass stdscr.erase() stdscr.addstr(4, 3, key) stdscr.refresh() curses.wrapper(main)
- the nodelay call is mentioned in the tutorial as letting things continue if no key is pressed (good for animation) - i tried calling clear() in the loop but it makes flicker. changing it to erase reduces refresh, and then putting it after getkey() seems important; getkey() can refresh. it might be the call to refresh() is not needed if getkey is called.
yeah the extra call to refresh() can be removed. ok, next we'll need the width and height
the tutorial says this: Your application can determine the size of the screen by using the curses.LINES and curses.COLS variables to obtain the y and x sizes. Legal coordinates will then extend from (0,0) to (curses.LINES - 1, curses.COLS - 1). we'll also need the character aspect ratio to draw things that look right. the aspect ratio of a text terminal is usually about 1/2 . the same syscall that gets the lines and cols actually reports this information (in the form of pixel count), so i'll probably store it as separate width and height aspect variables
$ vim README.md vim is a text editor where you use hjkl to move the cursor around instead of arrow keys (a mouse works but it doesn't make sense to use one with vim). in order to change the text, you have to hit 'i' to switch to editing mode, and then 'esc' to switch out (long ago 'esc' was near the other keys). vim is much nicer when dealing with physical issues, such as a cramped environment or motor control problems or a risk of carpal tunnel injury, because it never requires pressing more keys than one at a time, and the keys are picked to be physically near each other, requiring less hand movement. i picked vim or emacs when i started using the OLPC XO 1 outdoors a lot. tiny rubber water-resistant keyboard, was hard to press ctrl too much. $ git add README.md # oops fatal: not a git repository (or any of the parent directories): .git $ git init $ git add README.md $ git add test.py $ git commit -m 'initial commit' $ gh repo create
Push an existing local repository to GitHub ? Description an attempt to make a small #D graphics engine in python ? Add a remote? Yes ✓ Pushed commits to https://github.com/xloem/ascii3d_deadman.git
I failed to add the README, typed thec ommand only into email, not into terminal. I force-pushed a new commit with the change. Doing so is improper in two domains, and hides details on the ongoing community destruction.
a saving grace is that github does keep a history of such behaviros, and it is publicly accessible i did try to scrape this history once and found it full of corruptions
i used the github gui interface to add the word 'ascii' to the description which was horrendously missing. this is not intended to be a real 3d engine.
i put it in a class for object-orientedness. this makes it seem bigger textwise. then i derived from the class to draw the text centered now. here's the class: class Engine: def run(self): curses.wrapper(self.__run) def __init(self): self.window.nodelay(True) self.window.clear() def __run(self, window): self.window = window self.__init() while True: self.update(self.__update()) def plot(self, x, y, str): line = round(y / self.char_height) row = round(x / self.char_width) self.window.addstr(line, row, str) def __update(self): # getting a key also refreshes try: key = self.window.getkey() except: key = '' # wipe for next draw self.cols = curses.COLS self.lines = curses.LINES self.char_width = 8 self.char_height = 16 self.width = self.cols * self.char_width self.height = self.lines * self.char_height self.window.erase() return key
here's the code that uses it: class App(Engine): def __init__(self): self.last_key = 'press key?' def update(self, key = ''): if key: self.last_key = key self.plot(self.width / 2, self.height / 2, self.last_key) if __name__ == '__main__': App().run()
ok, ummmmm, although it is easy to make a vertical line, to make it look interesting we'll need something more. maybe we could start by plotting the periods at the base? this seems somewhat reasonable? and if the moribidity becomes bad, we could switch to plotting the stick figure standing, maybe in a crowd. maybe i'll use numpy for now to do math. and i guess i'll do a perspective projection. unsure whether with a matrix or not.
normal pictures where things are far away like a camera takes use trigonometry basically, everything has a depth coordinate: distance from the camera. and the size is roughly divided by the depth coordinate. this is correct for a 90 degree field of view and objects at a distance; it would be scaled for a different field of view, and tghe objects become warped when they get close.
more precisley, i'm pretty sure for a 90 degree field of view, the horizontal and vertical coordinates are scaled by the depth coordinate. so, to draw stuff in a perspective projection, you transform all the coordinate systems so that they are relative to the camera. i guess i'll see how confusing it is to use 4x4 matrices or something
i wrote this much: class CoordFrame: def __init__(self): self.mat = np.array([1.0,0,0,0],[0,1.0,0,0],[0,0,1.0,0],[0,0,0,1.0]) there are 3 things i'm likely to want to do: - place something at a specific location - orbit the camera around the origin (to animate seeing a scene from multiple angles) - transform something to be relative to the camera's view it's also nice to write code so that the camera can look at a particular point what seems most important to start with is transforming something to be relative to the camera's view i'll need to put some coping energy into remembering how to do that i guess
ok ummmmmmmmmmmmm camera has one coordinate system, of where it is and where it is looking. this seems at first a fine coordinate system for objects in its field of view. if it aims along an axis, that's the depth axis. ok ummmm to express just a vector translation relative to something, you subtract it. i think the matrix solution involves a matrix inverse. you can express translation via a matrix multiplication, so likely you express an entire transformation --- yes! that's how rotation works! -- yay, via matrix multiplication. and then you invert it.
VectorA = CoordFrameA * VectorB so CoordFrameA is the coordinate frame that VectorB is relative to, expressed in terms of the coordinate frame that VectorA is relative to. there's a way to think about it so there are fewer different frames and vectors everywhere confusing you, not sure what it is. so if the camera is at CoordFrameCamera, and we had something positioned in the world coord frame ....... what does this mean? CoordFrameCamera is the *multiplication* applied to things expressed *relative to the camera* to get to the world frame. WorldVector = CoordFrameCamera * CameraRelativeVector So, to move something to be relative to the camera, we multiply its world location by the inverse of the camera location. I guess it's usual when making a 3d engine to have a class representing a transformable object. Is there a basic operation happening here? We're moving between coordinate frames. If I have two coordinate frames in terms of the world frame .... okay so the basic operations here are expressing a world vector in terms of a local coordinate frame, and a local vector in terms of the world coordinate frame. transforming something to the camera expresses it in terms of the camera coordinate frame.
so i have this, but i don't like the name "destranform" and how the operation of a transform brings something _out_ of the space. maybe I'll change it to be something positive like "apply" or "__call__" and just remember how to use it or write in the comment how: class CoordFrame: def __init__(self, mat = None): if mat is None: mat = np.array([1.0,0,0,0],[0,1.0,0,0],[0,0,1.0,0],[0,0,0,1.0]) self.mat = mat def detransform(self, vec): return self.mat @ vec def inverted(self): return CoordFrame(self.mat.inverse()) # to move into camera space, call cameraframe.inverted().detransform(vector)
untested! class CoordFrame: def __init__(self, mat = None): if mat is None: mat = np.array([1.0,0,0,0],[0,1.0,0,0],[0,0,1.0,0],[0,0,0,1.0]) self.mat = mat def apply(self, vec): return self.mat @ vec def inverted(self): return CoordFrame(self.mat.inverse()) # to move into camera space, call cameraframe.inverted().apply(vector) # to move into outer space, call innerframe.apply(vector) next i'll make a class for single-point characters to place in 3-space, to draw the bottom
i guess you could make something cool just with 3d points. i haven't really decided on how to design it. i'm not sure whether to make a draw() function on it. i guess i will turns out implementing drawing will need a camera though hmmmm i guess it makes sense to have two passes, one where all the points are projected, another where they are drawn. since numpy does stuff in parallel
here's my point class draft. i'm hoping to get it to write text in 3-space: class Point: def __init__(self, str, pos): self.points = np.array([pos]) self.str = str def _points(self): return self.points def draw(self, engine, projected_points): x, y, _ = projected_points[0] engine.plot(x, y, self.str)
i'm starting to feel better. maybe i can have breakfast later/soon :) {{ yesterday i went to a music lunch and didn't eat anything from movement/intention issues, and people commented :S }} for the camera, i guess a camera is basically just a bare coordframe. this would let you reference objects (like the stickman's head) as cameras, kinda cool. but to make it orbit, we'll want more methods on coordframe. an orbit is basically a rotation rate, times a time duration, expressed as a coordinate frame around an axis. so to form an orbit transformation we'd need to be able to make coordframes ... i guess basically using euclid angles, maybe i can find a web link
euclid angles can have bad singularities but they make sense here since the plan is to artificially limit the engine so that the up direction never changes
_euler_ angles. not _euclid_ angles. oh those greeks. https://en.wikipedia.org/wiki/Euler_angles#Rotation_matrix long story short: the full expression of an euler angle matrix is ridiculous. it makes more sense to either make axial rotation matrices and combine them, or make rotation about an arbitrary axis.
more appropriate 3d coordinate frame rotation matrices are documented at https://en.wikipedia.org/wiki/Rotation_matrix#In_three_dimensions a general purpose matrix form for any axis is given at https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_... i'll copy that matrix into a method
This is what I have now. It would be more difficulty to hunt down bugs stemming from this method, so it's time to run it through the python interpreter and fix things. X = np.array([1.0,0,0,0]) Y = np.array([0,1.0,0,0]) Z = np.array([0,0,1.0,0]) W = np.array([0,0,0,1.0]) class CoordFrame: def __init__(self, mat = None): if mat is None: mat = np.identity(4) self.mat = mat def apply(self, vec): return self.mat @ vec def inverted(self): return CoordFrame(self.mat.inverse()) @classmethod def fromaxisangle(cls, axis, angle): # from https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_... cos_theta = np.cos(angle) sin_theta = np.sin(angle) axis = axis / np.linalg.norm(axis) return CoordFrame( cos_theta * identity(4) + sin_theta * np.cross(axis, -np.identity(4)) + (1 - cos_theta) * np.outer(axis) )
if __name__ == '__main__': frame = CoordFrame.fromaxisangle(Y, 0.2) print(frame) $ python3 -m pdb test.py (Pdb) cont it finds all sorts of errors i totally didn't see, so i go through and fix them.
ended up looking like this: @classmethod def fromaxisangle(cls, axis, angle): # from https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_... cos_theta = np.cos(angle) sin_theta = np.sin(angle) axis = axis[:3] axis = axis / np.linalg.norm(axis) mat = np.identity(4) I = mat[:3,:3] mat[:3, :3] = ( cos_theta * I + sin_theta * np.cross(axis, -I) + (1 - cos_theta) * np.outer(axis, axis) ) return cls(mat) $ python3 test.py [[ 0.98006658 0. 0.19866933 0. ] [ 0. 1. 0. 0. ] [-0.19866933 0. 0.98006658 0. ] [ 0. 0. 0. 1. ]] This is a reasonable rotation matrix about the second axis. Since it's rotating about the second axis, it changes only the first and third, and the values are perpendicular sines and cosines.
to test and debug displaying points, i'll want to add time tracking to the engine so that rotation angles can be calculated to be consistent with framerate
import curses, time, numpy as np ... class Engine: ... def __run(self, window): self.window = window self.__init() self.monotonic_start = time.monotonic() self.time = 0 while True: now = time.monotonic() - self.monotonic_start time_change = now - self.time self.time = now self.update(time_change, self.__update()) maybe it has bugs, maybe it works then i get to implement the scene. it's gonna need debugging.
commit 0f7743554293b82c6d3204f68f066adad1c2aa6c (HEAD -> main, origin/main) Author: John Doe <johndoe@example.com> Date: Wed Jun 22 11:24:21 2022 -0400 added time, organised a little -- okay, so for updating a scene, I'll also need a list of objects that the engine can gather points from to transform. i'm getting closer to having breakfast!
ok ummmmmmmmmmmmmmmmm how does the engine know what the camera is to transform points? the scene should tell it. the scene is a subclass. the engine calls the scene's update() function. maybe there can be a method to set the camera. unfortunately, intertwining of cameras and scenes. curses does support multiple windows. scene might be associated with run() function that handles window. not how it's presently set up. maybe scene can wrap Engine and pass the camera to run()
update: rather than a hanging image, considering having it display 3d text saying 'karl is an idiot'
i got confused while adding objects to scene and transforming points, so likely more bugs from missing things during changes
going to implement Scene now to show some points. it still derives from Engine, not separated. it returns the camera to draw eahc call. objects can be passed to Engine() or Engine.add() to put in scene doesn't work yet!
to place the camera I'm adding a position= keyword parameter to CoordFrame.fromaxisangle(). to put it in a 4x4 matrix, i have to decide whether to put it in the last column, or the last row. i think it depends on whether i am premultiplying or postmultiplying to apply the transformation. i'm premultiplying. i guess i'll just open it up in the interactive terminal and see which one works.
i have trouble thinking about matrices because i was building an app using linear algebra research papers in 2014
regarding camera placement: i forgot about whether translation comes before or after rotation in one interpretation, the system orbits the origin, in the other it rotates about its local center i better see which one i implemented with another test committing for now since behavior is destabilising
i'm not sure whether i will continue / finish this, but it seems cool. i want to remove the references to death from it, maybe switch to positivity for a bit, unsure. - i reversed the order of matrix multiplication. this lets stacks of vectors be processed unmodified by passing to apply() - i fixed some typos - i have not shifted draw coordinates to be at the center of the screen, so it draws offscrene - something is wrong where it is mapping depth to height in a way i don't expect addressing depth being height seems next issue
the new plan is to have it plot text in 3-space that says 'yay! wonderfulness!' and then another text that says 'we can all get along\n\ni just know it!'
right now it throws an error trying to tell curses to move offscrene, so i have it open in pdb. it's easier for me to see what the errors are when they break to a debugger. it would make sense to step through the process of transforming the world coordinates to screen coordinates. this would show how depth turns to height.
ohhhh i think i figured it out the transformed point is [ 0., -10., 10.] the original point is [0,0,0] and the camera is CoordFrame.fromaxisangle(Z, 0, [0,10,-10,1]) so this makes total sense; the camera is looking head-on and is 10 units above the object, so the object appears at -10. it looks like i'm failing to divide the coordinates by the distance. maybe do that.
ok that worked out index f714541..f12338e 100644 --- a/test.py +++ b/test.py @@ -93,6 +93,7 @@ class Engine: if len(self.object_points): untransformed_points = np.concatenate(self.object_points) transformed_points = inverse_camera_frame.apply(untransformed_points)[:,:3] + transformed_points[:,:2] /= transformed_points[:,2:3] for idx, (object, range) in enumerate(zip(self.objects, self.object_point_ranges)): object.draw(self, transformed_points[range[0]:range[1]]) # getting a key also refreshes @@ -117,7 +118,7 @@ class Engine: class Scene(Engine): def __init__(self): self.last_key = 'press key?' - self.camera = CoordFrame.fromaxisangle(Z, 0, [0,10,-10,1]) + self.camera = CoordFrame.fromaxisangle(X, np.pi/2, [0,10,-10,1]) self.text_object = Point("press key?", [0,0,0,1]) super().__init__(self.text_object) def update(self, time_change, key = ''): it shows at 0,0 now.
the displayed text plan is changing from 'yay! wonderfulness!'. not sure what it will be next. certainly 'we can all get along' will be less publicly shared knowledge.
maybe text along the lines of 'just a hobby project so i can do something other than flail my limbs around'
i'm kinda paused. i'm at the point of writing a screen matrix to move to the center of the screen . i was thinking of holding a 2x2 matrix as an object member. the plan is to divide the width and height by two, and take the minimum and call it dim. then to scale by dim (first column of matrix) and add the halfwidth and halfheight (second column of matrix) so maybe like screen_mat[1,:] = (self.width, self.height) screen_mat[1,:] *= 2 screen_mat[0,:] = min(screen_max[1:]) dunno
i'm confused around the screen matrix can i do this with a 2x2 matrix? how do i do it? it's not a 2d transformation matrix if it's 2x2. that would be 3x3. when i think of the 2x2 matrix, i imagine it applying to an equation: the first column multiplies, the second adds. then i can put input things in. maybe it isn't matrix multiplication i'm thinking of. maybe it's just normal broadcast multiplication. or something? i think i would need the points expressed as a 2x2 matrix themselves, for this to work. with 1s underneth them to add. uhhhhhhhhhhhhh what way makes sense to do here? - i have two data items in the form of a vector [x,y,w,1] - i care only about x and y - i want to perform a linear transformation to both x and y - the vectors i have are in a matrix stacked [v1,v2,v3,v4] so what makes sense here? we could do it in two operations, multiply and add there's likely a way to do this with a matrix there are likely other operations that would do multiply+add in numpy, but they may be less intuitive to read in the context of graphics code. so maybe i'll make a 3x3 matrix instead of 2x2. that might make sense. or a 3x2.
i roughly did that. it's not working yet, bugs remain. something i do more as a norm now is `import pdb; pdb.set_trace()` where-ever i want to look at information to resolve the error. this seems to really ease debugging when very confused. no need to do the breakpoints/conditions dance. no need to worry about whether or not i ran it in a debugger. can just code the condition right in. i'm thinking this would probably work in other languages. link to a a debugger, open it when the assertion fails. i'm sure people do that already in large projects, maybe i used to. but now it seems a good norm. one of the reasons i avoid nodejs is the difficulty i used to have debugging it (and a frightening interpreter error i ran into). i bet there are a lot of nodejs debugging libraries now, that could be just opened when a chunk of code is added in.
e1a03cdeeee4ccfbe22689650212489e53053344 it now displays 4 points in a square in 3-space on a ground plane, and when run one can just barely tell that it is actually 3d in the TUI. which i'm sure has been done before, but is fun to have implemented. maybe it would be most productive next to make stick figures. first camera movement though. maybe i can use hjkl for the camera like in vim. but maybe also wasd. it's such an accomplishment to have made something that it's hard to continue!
it now orbits the camera when keys are held. d4840a326cdf610f00509d61f468ada50904a17c uhhh it uses the key repeat rate to do so. i guess it could be nice to measure the fastest repeat time, and wait twice for that to register a release, so it moves smoothly every frame isntead of when the repeat time happens. if instead i used an event device, it wouldnl't work on windows/mac/anything esle. there are also terminal codes for getting mouse information on stdin.
participants (1)
-
Undiscussed Horrific Abuse, One Victim of Many