Arundel Gauthier's portfoli
o
Table of con
T
en
1 Introduction
.....................................................................................................................................
4
2 Programming & Hardware
..............................................................................................................
6
2.1 FODT to HTML converter (Javascript)
.................................................................................
6
2.2 Mijnkatgoriezoektviz (CEF, pywebview, Transcrypt, Parceljs)
.............................................
7
2.3 Imquloco (Application Development Enhancing Framework)
............................................
10
2.3.1 Version 1.x.x (documentation bit)
...................................................................................
11
2.3.2 The code (video)
.............................................................................................................
13
2.3.3 Version 2.x.x
....................................................................................................................
14
2.3.4 The code (video)
.............................................................................................................
15
2.3.5 version 3.x.x (code video)
...............................................................................................
16
2.4 Query Paths (Sunacle)
...........................................................................................................
17
2.4.1 Some ideas I had on Query Paths v1.x.x
.........................................................................
17
2.4.2 Some more ideas on syntax and API’s
............................................................................
19
2.4.3 A tiny bit of implementation code
...................................................................................
23
2.5 Website ’Restaurant Delicaato’ (HTML, CSS, Javascript, NodeJS, Express)
....................
25
2.5.1 Demonstration video
.......................................................................................................
25
2.5.2 Database wrapper code
...................................................................................................
26
2.6 Advanced keyboard functions language
...............................................................................
27
2.7 Python Regexes that fix Transcrypt bugs (pre/post-processor)
...........................................
33
2.7.1 Small part of code
...........................................................................................................
33
2.8 svgwrite svg file loader
.........................................................................................................
35
2.8.1 The script
.........................................................................................................................
35
2.9 List of opinions on bad parts of webtechnology standards (XML, HTML, DOM)
...........
37
2.10 3D affine polygon projection engine (love2d)
...................................................................
38
2.10.1 Version 1
........................................................................................................................
38
2.10.2 Version 2
........................................................................................................................
39
2.11 3D polygon perspective projection engine (C#, OpenTK, GLSL)
....................................
40
2.11.1 Video demonstration
.....................................................................................................
40
2.12 3D raycasting voxel engine (love2d)
..................................................................................
41
2.13 3D raycasting polygon engine (C#, OpenTK)
...................................................................
42
2.13.1 Demonstration video
.....................................................................................................
42
2.13.2 Demonstration images (higher resolution)
....................................................................
43
2.13.3 The code (video)
...........................................................................................................
45
2.13.4 Programming design decisions and manual
..................................................................
46
2.14 GUI editor plugin (Roblox)
................................................................................................
48
2.14.1 The interface
..................................................................................................................
48
2.14.2 An example of GUI's created with the editor
................................................................
49
2.15 Model Texture Mapper plugin (Roblox)
............................................................................
50
2.15.1 The interface
..................................................................................................................
50
2.16 Camera Character Collision Physics simulator (Roblox)
..................................................
51
2.16.1 The interface
..................................................................................................................
51
2.17 Polygon to voxel converter (Roblox)
.................................................................................
52
2.17.1 Some example conversions
...........................................................................................
52
2.18 Mesh collision detection (Roblox)
......................................................................................
53
2.18.1 Example of a collidable mesh
.......................................................................................
53
2.19 Scripted particle effects (Roblox)
.......................................................................................
54
2.19.1 Some example particles
................................................................................................
54
2.20 Paint (JavaScript, HTML)
...................................................................................................
55
1
2.21 Tesla coil turret (Roblox)
....................................................................................................
55
2.22 First person shooter demo (Roblox)
...................................................................................
56
2.23 Rock jumper game (Roblox)
...............................................................................................
56
2.24 Tic-tac-toe (Roblox)
............................................................................................................
57
2.25 Animation tool (Roblox)
.....................................................................................................
57
2.26 Build tool (Roblox)
.............................................................................................................
57
2.26.1 The interface
..................................................................................................................
57
2.27 Minimap (Roblox)
...............................................................................................................
58
2.28 Triangle Extrusion Plugin (Roblox)
....................................................................................
58
2.28.1 The interface
..................................................................................................................
58
2.28.2 Before the extrusion
......................................................................................................
59
2.28.3 After the extrusion
.........................................................................................................
59
2.29 PAM converter
.....................................................................................................................
60
2.29.1 Code
..............................................................................................................................
60
2.30 Delta robot plays tic-tac-toe
................................................................................................
62
2.30.1 Converting platform position to servo angles
...............................................................
62
2.30.2 Code
..............................................................................................................................
63
2.31 Shape recognition algorithm
...............................................................................................
64
2.31.1 Surrounding pixels
........................................................................................................
64
2.31.2 Nodes
............................................................................................................................
64
2.31.3 Conditions for the type of shape
...................................................................................
64
2.32 Driving robot that follows a curve & removes obstacles
.................................................
65
2.33 Driving robot that detects a laser & leader robot
..............................................................
66
2.33.1 The hardware
.................................................................................................................
66
2.33.2 Operation scheme
..........................................................................................................
66
2.34 Touchscreen Ship Navigator
...............................................................................................
67
2.35 Radar (Arduino C, Spyder)
.................................................................................................
68
2.35.1 Demonstration Video
....................................................................................................
68
2.35.2 Output Environment Graph
...........................................................................................
69
2.35.3 The data acquisition code
..............................................................................................
70
2.36 Quad image to square function
...........................................................................................
73
2.36.1 An example perspective conversion
..............................................................................
73
2.36.2 The code
........................................................................................................................
73
2.37 .mesh to .obj converter
........................................................................................................
76
2.38 Factory flow monitor (love2d)
............................................................................................
77
2.39 Some Powerset Algorithms
.................................................................................................
78
2.39.1 In Python
.......................................................................................................................
78
2.39.2 In Lua
............................................................................................................................
79
2.39.3 How it works
.................................................................................................................
81
2.40 Simple Collision Detection Maze (HTML/CSS/Javascript)
..............................................
82
2.40.1 The code
........................................................................................................................
82
3 Math & Physics
.............................................................................................................................
87
3.1 Understanding the relation between Cartesian and Polar Fields (Application in Desmos)
.......................................................................................................................................................
87
3.2 Understanding Double Integrals in Polar Coordinates
.........................................................
88
3.2.1 My application in Geogebra
............................................................................................
88
3.2.2 Code in Javascript
...........................................................................................................
90
3.3 Grid Transformer (Javascript)
................................................................................................
93
3.4 Parametric Curves on Surfaces (CalcPlot3D)
......................................................................
94
4 Programming Communities
...........................................................................................................
95
2
4.1 Bug reports for Inkscape
.......................................................................................................
95
4.2 Bug reports for Pywebview
..................................................................................................
95
4.3 Bug reports for Brython
........................................................................................................
96
4.4 Bug reports for Transcrypt
....................................................................................................
96
4.5 Bug reports for svgwrite
.......................................................................................................
96
4.6 Bug reports for cefpython
.....................................................................................................
96
4.7 Bug reports for mrab-regex
...................................................................................................
96
5 Mechanical Builds
.........................................................................................................................
97
5.1 Flywheel
.................................................................................................................................
97
6 Designs (models, effects, scenes, games)
......................................................................................
98
6.1 Uncategorized
........................................................................................................................
98
6.2 “Free energy” rising and falling weight carried by floater
...............................................
103
6.2.1 Schematic
......................................................................................................................
103
6.2.2 The variables and their metadata
...................................................................................
104
6.3 Drawings
..............................................................................................................................
105
6.3.1 No reference / imagined
................................................................................................
105
6.3.2 Reference picture hand traces
.......................................................................................
107
6.3.3 Reference object distant sketches
.................................................................................
108
6.3.4 Collages
.........................................................................................................................
109
3
1
Introduction
This is not a Curriculum Vitae.
The programming projects in this portfolio use the languages: C, Python, Javascript, CSS, SVG,
HTML and Lua.
Descriptions may contain inaccuracies, sometimes due to the lack of me having
documented the subject, and having to create it by recalling it and re-a
q
uainting with it years later.
It should be underst
ood
that many subjects have been started as a means to an end for some other
subject, which may not be published here; or, was for understanding part of some theory; or, for
learning a
feature of a
language;
or I was >12 years old;
and
due to th
ese
,
the documentation
and
completeness
may be
lacking
a lot
as judged from my current experience level
, but the subject is
still worth showcasing to friends and employers to get an impression of my history;
most of my
projects and statements
showcased
do not represent my full effort
that I wish I could exert given
more time;
none of the
se
showcases
I would regard
as largely
to fully
finished
or coherent
;
as such,
while it is published on a website that is publicly accessible, it is intended to be shown to a group of
people selected by me, and judged by them
with an expectation far from specification-, mass-
publication- or education-level readyness
.
After all, the world sometimes nessecitates a person to
share the emb
a
rassing incoherent thoughts they have had and refined
during
awfully
inconvenient
time periods and
narrow time
spans
,
to keep the body alive (i.o.w: “make a living”)
.
The intuition of
relative certainty of completeness
a
person states to have about their product is one
important factor when judging.
While I am speaking about “judgement”, I understand what it is,
but I do not know the
categorization of its parts, nor
know
when
a
specificity or
generality
about a
person’s personality or
skills
is
allowed to be made from their showcased products:
“
person/
product
X
is
good,
of
high
quality, original,
creative
”,
even if they were to state some relative certainty of completeness.
You
will still hear me do it
though in some cases
.
My language may contain praise to
wards
someone’s
music or jokes for example, but it is probably a representation of expressing my
satisfaction
with
the experience; I may subconsciously even store the praise as
a
real enhancement of their
personality’s representation in my mind, and be amazed when th
at
person walks into the room
(fanning-out); but to have it mean “you/
this thing
is
so creative!”, I can not do that
with much
certainty
.
It would require a long quest for evidence
I believe
.
That should not be an insult.
Judging
skill/personality is not my field.
But someone who experiences a representation of the product
s
, like
in
this portfolio, might
do so
.
Thus, it is in my interest to add some wisdom to the judger
(you too,
recruiter)
- if they did not take such into account already -
to achieve fairness. But the rest is up to
you,
because again,
it
is so not my field
.
On the other hand, I sometimes do relative low quality practical judgements about products I
bought.
There are two meanings to that: the judgement is of low quality, and about low quality.
For
example, a computer mouse I have has its left button stuck a lot, the power switch can barely move,
the cursor lags
and the clicks lag for max ~7seconds
from day one; the battery is drained after a
week of use, and the ink that says “Logitech” has half faded after ~6 months. Can I call this mouse
producer
low quality?
Yes, but
more accurately,
practically low relative quality. If I were to remove
“practically”, and you would interrogate my justification for claiming “low relative quality”, I
believe it would fall apart; I simply do not
know how to properly judge quality (I consider this a
lifelong quest; but I can try and I do try!), but even if I try to judge, I do not
care enough to
properly
research the alternative view that “
it is a relatively high quality mouse
producer
, because the one
that I received is accidentally very defective
(click and power button)
, and it
is
the battery’s fault
for running out of power quickly
,
it is the chained
USB-port-
hubs that causes the lag – some
devices connected to that hub display lag too - and it is my feet that rubbed the ink off because
4
I operate them with my feet, and from my experience, the
re is
abrasive sand
on their
surface
more often than on
my
hands – for which it was not designed,
and the brand may be a clone of
Logitech, so I can not blame them
...
“.
I
have
indeed acknowledge
d
some of the critiques
being
possible
after having thought about it for some time, off and on:
fine; I did not blame Logitech
because it could be a fake
, but there
c
ame
a time
at which
I stop
ped
caring about this truth,
for
example when proving the counter-arguments bec
ame
too hard, low gain (I can buy another
mouse
easily;
and while I value gaining judgement skills
and being ethical
by
having thought up possible
explanations
, it is so hard to know when this
is going to
help me
or the other party
).
So I made
some
claim -
not the harshest, but I leave room for toning down even more -
in my mind and now on
paper,
and I would on the street
talking with low volume, that the mouse works horribly and this
mouse producer sucks
(not the brand)
,
and unfortunately speak with shorthands such as “low
quality” as if I did a proper judgement,
while I mean
“
relative
practical judgement”.
Maybe I should
use the claim “
this mouse is unusable”, which is the most objective, and does not imply the cause
residing within?
Again, this is
not
my field, but I can try it out on in theory low
expectation media.
T
he medium on which the claim could be published is very important to determine whether I will
share it; I believe I need to do more research if I were to write it as a review
(or yell it through a
loudspeaker on the street)
;
I would buy multiple instances of the mouse. As you read about my
claim here, as part of my portfolio, you will notice that in this case, the review will be of higher
relative quality than as portfolio text, and if I were to do the review, the claim in the portfolio may
become transformed or nullified, because I should remember the best research I did for the higher
relative quality medium if I want to have it synchronized with the lower relative quality medium.
This is just to make a point; I do not want to generalize which would be the higher quality medium.
But in theory, a judger should research whether our expectations of the permitted relative quality
per statement for some medium is in phase, and if not, who is right about their expectations.
Besides that, I do not have fixed categories of an audience in mind. If
I like them, and
the
y
can
understand almost none of it, I will still share it with them;
I will then (irrationally?) ask them
whether they like it
.
If they can not understand 99% of it, they
should
just tell me “I like this part”;
I
will not pressure them to judge deeply
or justify it
; and they
might
be comforted by my guess that
more than 70% of the statements I show are not even meant to be understood by experts of the
relevant fields, and sometimes myself;
therefore, unelaborated criticism is likely acceptable.
Although I might ask for an elaboration, I will likely agree with you
when you judge the content
.
This happens when you are constrained but want/need to share
your work
!
But there is also the
get-
praise
d
-by-friends-now-ish factor! Especially the “now” part is
desirable
: how long can you stay
happy
during the private development period of your to-be-shared-and-praised-thoughts
, if the
praise is your main hapiness giver, when the praise comes in 1
up to 100000 years?
This
makes you
share
early unrefined
updates.
But then there is the “ish” part, that suggests that there are situations
whereby a late delivery - hopefully accompanied by high relative quality – yields the larger
happiness
or some other category of value
.
This makes you share
late, more refined
updates.
For
now, these category of schedules have been and will be balanced by my intuition.
The purpose of
this portfolio is to showcase my history and skills,
not just the highest quality that lies in the very
recent past
and for which I have had the most development time
. Thus naturally, with my current
experience
, I would call
many
subjects low
relative
quality in here;
but that should not stop you
from forming an impression of my skills.
( Are you one of those people entering
hype-seller simpleton Pavlov dog
’s judgement mode when
hearing me call my work “low quality” in the introduction of a portfolio? )
5
2
Programming & Hardware
2.1
FODT
to
HTML
converter
(Javascript)
I made an FODT to HTML converter. FODT is an open text document format that is part of the
OpenDocument specification.
I wanted to display office documents on the web,
preferably on a
custom domain.
After
having tried uploading to Dropbox,
Google Drive
and TiinyHost
for their rendering
capability
of office documents,
hundreds of converter websites and piping multiple
intermediate
format
conversions together
(
DOC
,
DOCX
,
PDF
,
SVG)
, and trying the HTML export function in
Libre/OpenOffice,
plugins for them,
and some other office
desktop
renderers, I could not find one
that supported videos,
selectable text,
modular relative resource linking (with the implication of no
resources that could be opened in a new tab,
longer load times due to large network traffic)
, and
was
bug free
in their output
.
I solved it
largely (for my use case at least), in a semi-automated manner, by having created a
program that does some conversions and using third party tools via the GUI for other conversions;
do with the input FODT file
the following
(
ꖘ
= previous transformation result;
ⵙ
= first input)
:
1)
R
un my program that replaces
ⵙ’
s
videos
(draw:plugin)
and images
(draw:image)
with
hyperlink images
(draw:image nested in draw:a)
(in step 4, Dropbox does not accept videos
in PDF, and images are merged with the background, so to prevent this, the information is
stored in this convoluted manner)
2)
Convert
ꖘ
to PDF with LibreOffice
3)
Upload
ꖘ
to Dropbox and view it in there; it will be converted to HTML. Dropbox will
convert
ꖘ
's draw:a's to HTML a
4)
In Dropbox, beautify
ꖘ
'
s
pages by removing redundant elements
5)
P
revent the removal of pages by Dropbox's scroll-unload script by running
my program
(it
adds a MutationObserver that puts li tags with attribute data-index="..." back in the DOM.)
6)
R
un reinstantiate_pages after having scrolled to the bottom
7)
Replace
ꖘ
's HTML a tags with HTML resources by running my program
8)
Beautification
9)
Beautification
10)
Save the page of
ꖘ
to a folder with SingleFileZ (options: HTML content --> save original
URLs of embedded resources = true)
11)
Replace
ꖘ
entry's resources src attribute's values with the resources data-sf-original-src
attribute's values by running my program
This is not an exact copy of the algorithm.
Much is paraphrased because the documentation is not
really ready to be read.
By the way, this portfolio was converted from FODT to HTML in this manner!
6
2.2
Mijnkatgoriezoektviz
Mijnkatgoriezoektviz
(CEF,
pywebview,
pywebview,
Transcrypt,
Transcrypt,
Parceljs)
Parceljs)
This is an application that can be used to mine, categorize, analyze, visualize (with svg and html)
and search through data by programming in a visual, data-driven, flow-based and schema-based
abstraction language on top of some existing programming language (with default support for
Python and Javascript), using pipelines (abstract or flat) – which are classes with in- and outputs
and state – as the code (abstraction) containers. It is schema-based in the sense that you can draw
the symbology and thus meaning of the abstraction pipelines. For instance, you could define a logic
language such as electronic logic gate diagrams with its symbols in Python and program in it in a
flow/circuit-oriented representation.
The abstraction language uses classes for multiple purposes. A class can have zero or more
in/outputs and it retains state. If it has zero in- and outputs, it acts as a conceptual container. If it has
more than zero I/O's, it acts as an abstraction container. If the class is a leaf node in its class
hierarchy and it has more than zero I/O's, then it acts as a stateful function. A stateful function
contains source code. Any class that is not a leaf node can not directly contain source code.
An abstractly-related class hierarchy is a tree structure of classes whereby each class is broken up
into component classes that are its children. A parent and its child classes are related by higher and
lower levels of abstraction. The relation lays in a theoretical “summation” of those component
classes, such that the component classes form a chain of processes that can be viewed as a “whole”
process that is the parent class; and the relation lays in the passing of input data of a parent to the
input of its entry child, and the passing of the output data of the exit child to the output of its parent
– which is the structure of a pipeline. This pipeline has a special property: its entry and exit points
(start class and end class) can connect to lower and higher levels of abstraction. Hence the term
“abstraction pipeline”.
An abstraction pipeline is akin to two interconnected black boxes that have a child-parent
relationship. A flat pipeline consists of interconnected black boxes in a sibling relationship, which is
one level of abstraction. A combination of a flat and an abstraction pipeline forms a program with
multiple levels of abstraction. To make a black box turn white (meaning to reveal its lower level of
abstraction), the user zooms in on their code by scrolling, which reveals a lower abstraction pipeline
visually on the same spot in their code, similar to the zooming in on a geographical map whereby
the more specific area names are revealed.
Existing modules of some language's ecosystem can be wrapped with the abstraction language, such
as Python's Regex module, so that the ease of programming with the module is enhanced by
expressing it's usual API as an abstraction pipeline. A helpful aspect of the abstraction language is
the re-use of output variable names from one abstraction pipeline in the input of another by drawing
a connective line, and the use of anonymous classes and I/O that you don't feel like naming, for
instance because of adequate context with your own defined symbology.
The pipelines can be multi-layer abstractions like a collection of Python classes can be, but the
abstractions are coupled: in Python a class isn't a pipeline that passes input data from root to leafs,
but in the abstraction language it is. Classes are seen as abstraction-related and/or conceptually-
related pipelines that may have a stateful function (thus with I/O and storage) that can pass data
from the root class to its leaves and back.
In written Python code, a class definition has one level of abstraction (class name and methods) that
is visible to the eye, but it isn't standard to drag other classes inside of them to form a visual
7
conceptual class name hierarchy. If the common coding style is followed where all classes are in the
top-level namespace, Python doesn't allow duplicate class definitions (atleast, overwriting occurs)
while conceptual hierarchies often contain the same node names in different branches. In theory you
can put a Python class inside another class, which enables a unique scope for the child class to be in
and thus a non-unique class name. That would allow for proper visual conceptually-related class
hierarchies in traditional text form. In the abstraction language such “class in a class” hierarchies
are the default way of organising code, but with the benefit of flow-based programming and the
hiding/showing of the classes on demand without your code collapsing.
In Python, to get an abstractly-related class and instance hierarchy (abstraction pipelines) in your
code, you have to do some manual labour to connect the input data of a parent class to the inputs of
its entry child. You'd have to add the magic method
__call__
to each class so that classes act as
functions and I/O data passing between them can occur, and you have to add code in each
__call__
that calls that class' child or sibling classes with appropriate arguments, or update some list of
inputs, and do that recursively until you reach the leaf function that may then return arguments.
Then you'd need a mechanism in the class that allows I/O passing to the next class under some
condition, for instance if all its inputs have changed or just some of them have. Part of the
abstraction language allows (abstraction) pipelines to be constructed graphically with the
aforementioned functionalities, with more ease and in a more central place on your screen than what
would be the case with text-based Python code.
The application is partly an SVG/HTML/XML editor. The DOM editor (workspace) is an essential
tool to edit and organize your drawing and scripts with several node selection and manipulation
functions such as move up/down; indent/dedent; select children; select descendants; group/ungroup;
search; show/hide: namespaces, node names, semi-unique names. In a “review” window you can
create questions (categories) about your data that you can then answer manually by reviewing each
data piece or have them answered (categorized) by a script. Data can be gathered with a built-in
HTTP node from some server or with a file reader node from your harddisk. You can also create
your own nodes that can do these tasks.
XML and DOM's limitation is their id system. Every ID has to be unique, so that searching for an
element happens in a global scope. There are legit reasons to give nodes in the same hierarchy
identical names.
Categorically it makes sense. For example, an orange and an apple both have a
crust. You'd like to have a fruit hierarchy. Fruit is the parent of orange and apple, and orange and
apple are children of fruit. So you have:
fruit -> apple -> crust
and
fruit ->
orange -> crust
. ID collision already! Crust must be unique. Ok, so we name them crust1 and
crust2, eh, right?
Issues with id's
:
•
it could look like you imply that oranges have two crusts (crust1, crust2).
•
it could look like the orange had one crust instance in the past, and it got removed and it got
a new crust (crust2).
•
name collisions: it's as bad as having a global id namespace for nodes that live in the same
hierarchy.
It really makes creating an application or drawing difficult. Imagine all your OS file names and
folders had to be unique. You'd have to be aware of all names in the filesystem, and even worse,
you'd be forced to invent unique names for every file. Then you lose a lot of value in using a
hierarchy. Yikes.
8
Querying an element by its path is cleaner and more maintainable than to try to come up with a
unique id for each element in larger applications. In this application, you can query an element with
a hierarchy of semi-unique names, such as
fruit/apple/crust
. The semi-unique name is an
attribute on an element with a custom XML namespace.
The application is written in Python, which is transpiled to Javascript pre-runtime using Transcrypt.
The backend is a browser engine: Chromium Embedded Framework with Python (cefpython) and
on top sits Pywebview. The interface was made in Inkscape and Notepad++ and BaseX & Xquery
was used to refactor some bits.
9
2.3
Imquloco
(Application
(Application
Development
Development
Enhancing
Enhancing
Framework
Framework
)
I made a software framework to enhance an application developer’s workflow; it consists of
Im
port,
Qu
ery,
Lo
cality and
Co
mponent
modules
.
At first, i
t was written in Javascript,
and
intended
to enhance Javascript,
browser API’s,
CSS, SVG, XML and XHTML
(front-end)
,
but later
(v5.x.x+),
Powershell was added,
and many enhancement
features are planned to be made usable
through a storage’s file system instead of through
the aforementioned languages
,
thereby
opening
up the possibility to enhance any file format.
Versions 5.x.x+
features;
either existing or upcoming
Im
port:
environment variables that other Imquloco modules provide for Powershell scripts, are
hidden for the programmer by psm1_imq files (I.o.w. boilerplate code abstraction
by running a
pseudo-profile
);
file imports
of executed
template
instances
are loaded automatically in
to
RAM
when they are in child folders of the template
instance;
Qu
ery:
an ident
ifier
language
for
concepts
through (nestable)
paths and hierarchies
with relation
type indicators
(Hiepat)
(
can be embedded in variable
or folder
names)
Lo
cality:
script environment
navigation
variables
to
local files
;
Co
mponent:
a hierarchical folder pattern with some relative symbolic link and junction ‘
parameters’
pointing up
to
act as
a
(template)
function
that can be called by
linking it in a
wrapper folder
in
some location and putting
‘argument’ folders
in the wrapper and pressing ‘run
file with [language]’
in
the
OS context menu
;
(
UI
)
data binding; local styles
, life cycle methods, network fetch
management, hierarchical load screens that wait for descendant components to load, network data
ready and error events
bound to UI
Other unnamed
:
•
new file system operations,
like: indent
and
dedent
node
;
run
program
with custom
CLI
debug window
(which shows module dependency chains, and module locations as syntax
highlighted Hiepats);
•
state management
•
module test automation (through input combination generators);
•
automatic
refactoring tools, like: Hiepat reference update in folder content and name when
the source path changes
•
f
older network explorer with support for symbolic links, junctions and Ontology Modeling
Language
•
TLC (Thing Life Cycle): a folder containing
life cycle
information on its parent, such as:
versions
(with automatically generated dependencies’ (links) at some version)
, backups,
tests,
subgoals, discussion, voting,
Imquloco’s major versions:
have
wildly
different focusses
on each module and
wildly different
implementations, with some modules
(some level)
absent in one version, then
(some level)
present
in the next, or vice versa;
can at times be found at other places in the file system under different
names. For example,
on
some
intervals
in
the past
,
the
Query
module had
the name “Sunaclpath”,
then “Sunacle”, and then “Ontology Modeling Language”,
and
they
are
not
in
the Imquloco
package folder
.
The modules will likely have independent names and downloads if ever published.
Whether I will keep the name “Imquloco” is uncertain,
because there are unnamed features that do
not exemplify th
at
name.
Version 1.x.x & 2.x.x were discontinued and old; version 3.x.x is radically different, so I am
comfortable sharing the older ones. In 1.x.x
there
are
some
unfinished
and unimplemented features
,
and
in
2.x.x
a lot.
Parts of the code are blurred so that
running it
in working condition is hard
for
thieves.
10
2.3.1
Version 1
.x.x
(documentation bit)
This is the view of a small bit of
the
documentation written in HTML
, SVG and CSS
:
11
12
2.3.2
The code
(video)
13
Video loading stopped.
Video playback aborted due to a network error.
Video can’t be played because the file is corrupt.
Video format or MIME type is not supported.
No video with supported format and MIME type found.
Video playback aborted due to an unknown error.
This video is playing in Picture-in-Picture mode.
Pop out this video
More screens are more fun. Play this video while you do other things.
Loading:
0:00
/ 0:00
Off
2.3.3
Version
2
.x.x
A sketch of the software (and
unimplemented
parts)
that
I made in OpenOffice Draw
14
2.3.4
The code
(video)
(
I made the syntax highlighter
in
Notepad++
)
15
Video loading stopped.
Video playback aborted due to a network error.
Video can’t be played because the file is corrupt.
Video format or MIME type is not supported.
No video with supported format and MIME type found.
Video playback aborted due to an unknown error.
This video is playing in Picture-in-Picture mode.
Pop out this video
More screens are more fun. Play this video while you do other things.
Loading:
0:00
/ 0:00
Off
2.3.5
version 3.x.x (code video)
16
Video loading stopped.
Video playback aborted due to a network error.
Video can’t be played because the file is corrupt.
Video format or MIME type is not supported.
No video with supported format and MIME type found.
Video playback aborted due to an unknown error.
This video is playing in Picture-in-Picture mode.
Pop out this video
More screens are more fun. Play this video while you do other things.
Loading:
0:00
/ 0:00
Off
2.4
Query
Paths
(Sunacle)
Query Paths
(it might be called Sunacle) is
a
programming
library able to parse
syntax
which
allows
the
expression of a
proposition's
concepts
that
are nodes of a consecutive path
of some
hierarchy,
or multiple disconnected hierarchies,
using
concept delimiters
and
path nesting, and has
the ability to bake in the path on a node to make it independent (the node gets an identity)
.
Query
Paths are
useful when there exists no word to describe a concept,
and using a natural language
phrase is to
o
verbose - such as in program variable names, and it is part of some hierarchy with the
path leading to it consisting of known words.
The best known example is a file system path.
I
wanted to make it possible for CSS, XML and Javascript to have Query Paths.
V2.x.x is in the
making.
2.4.1
Some ideas
I had
on Query Paths
v1.x.x
17
18
2.4.2
Some more ideas on syntax and API’s
node_selectors; attribute_selectors; actions; filter; authentication
/ = level separator = goes level deeper
namepath = multiple levels containing names, with no operators (e.g. sibling access). Example: [name]/[sub-name]
grouppath = multiple levels containing groups, with no operators (e.g. sibling access). Example: [group]/[sub-group]
#[namepath] = destination namepath (destination meaning: last node is returned). Also known as: id.
.[grouppath] = destination grouppath
trip namepath = a namepath that may be interspersed with operators. Trip meaning: the trip along the path is important; every name is used, filtered, or some other
operation is performed that is noted next to the name.
trip grouppath = a grouppath that may be interspersed with operators. A grouppath can consist of real groups and virtual groups.
selector = any type of path
level = one node identified by name and/or group between level separators or to the right of a hierarchy builder opening bracket "<"
$[group] = group
[name] = name
$[group1]$[group2] = level contains group1 and group2
$[group1],$[group2] = level contains group1 or group2
.[grouppath1].[grouppath1] = document contains grouppath1 and grouppath2
.[grouppath1],.[grouppath1] = document contains grouppath1 or grouppath2
![level] = exclude
* = access remaining descendants until new [level] expression
<[selector] || [level]> = hierarchy builder. Provides access to siblings
(string) = escaped string, may be used to embed a name consisting of a namepath or hierarchy builder
Selectors can not contain advanced functions or boolean operators like XPath, which keeps the syntax simple. That's the job of the API.
Attributes selection may be useful...
buffer = a system for automatically storing important variables?
Symbol for virtual hierarchies?
Chaining selectors in javascript: $("sel1").sunaclpath.get_last_child().$("sel2") ??
templates are static nodes.
@ and $ allow creation of hierarchy fragments
if [level] is a number, find an element with that name. Otherwise, find a node with no name but in the document order indicated by the number.
Sunaclpath markup attributes:
sunaclpath:name
sunaclpath:group
sunaclpath:gronam // sets a group and name at the same time
sunaclpath:groups // can be used for aliasing or for noting that a node is part of multiple groups.
sunaclpath:grouppath
sunaclpath:local-grouppath?
sunaclpath:grouppaths // can be added manually or is generated by generate_grouppaths. When set manually, and there is no group(s) attribute defined, the group(s)
attribute will be added based on the last group in the grouppaths.
sunaclpath:references
sunaclpath:delegations
sunaclpath:this // sets sunaclpath.this to the node with the last addition of this attribute to the document
sunaclpath:secure-boundary // a hierarchy separator that contains a password
sunaclpath:generate-grouppaths // values: "temporary", "accumulative", "permanent", "disabled". Generates grouppaths for its descendants as long as they have a group
and a continuous hierarchy. With a discontinuous hierarchy, the grouppaths are generated per continuous chunk.
"temporary" means that a node's grouppath will regenerate according to where they are in the hierarchy. "accumulative" means that a node's grouppath will regenerate
according to where they are in the hierarchy, and it will remember where it has been by adding the grouppath to grouppaths.
permanent" means that grouppath(s) are set once either by sunaclpath meschanisms or by the user and they stay that way; no grouppaths can be added or removed, unless
"permanent" value is changed.
sunaclpath:template_type // values: "repeat", "replace", "insert", "inherit", "merge_but_keep_template", "merge_but_keep_this".
sunaclpath:template_ref // clones a node depending on a selector, and allows for nodes and attributes to be added or overwritten. Will add a group attribute equal to the
name of the template. If there is a group attribute on the instance, it won't be modified.
sunaclpath (public, hasA):
buffer (public, singleton, hasA):
sunaclpath (public, singleton):
set(key, value)
get(key)
clear()
selector (public, multi-instance):
selector_list (public, multi-instance)
selector_filter (public, multi-instance)
node_list (public, multi-instance): // members that work on each node in the node_list, not the node_list itself. For direct node_list operations, use the language of
the hosting environment.
this
parents
first_child
last_child
previous_sibling
next_sibling
19
move_up()
move_down()
walk_up()
walk_down()
indent()
dedent()
remove()
swap()
contains(selector)
closest(selector) // closest ancestor of selector
append_child(child)
follow_references()
generate_grouppaths()
insert_before(selector || node)
get_nth_child(formula)
get_children(pierce_shadow_root = false)
get_descendants(pierce_shadow_root = false)
get_levels()
get_names()
get_groups()
get_indices()
get_references()
get_attributes()
get_attributes_partial() // excludes sunaclpath namespaced attributes
get_destination_namepaths()
get_destination_grouppaths()
set_contents(string)
set_names(string)
set_groups(string)
set_constraints(string)
set_references(selector_list)
_________________________________
// These sync methods have equivalents in sunaclpath.settings, but will overwrite this behaviour on a per node basis.
set_name_sync(attribute_name) // synchronizes an attribute name with sunaclpath:name. This can be used when your document format has some attribute akin to
the concept of a sunaclpath name that you don't wish to delete & replace with sunaclpath:name. Your document will thus remain compatible with Sunaclpath and its own
format.
set_group_sync(attribute_name)
set_groups_sync(attribute_name)
set_grouppath_sync(attribute_name)
set_grouppaths_sync(attribute_name)
set_references_sync(attribute_name)
set_delegations_sync(attribute_name)
_________________________________
set_attributes(object)
remove_attributes(list || string)
pure_node_list (public, multi-instance)
live_node_list (public, multi-instance)
new(selector || selector_filter) // editable nodes of type pure_node_list
clone(selector || selector_filter) // editable cloned nodes of type pure_node_list
live(selector || selector_filter) // editable live nodes of type live_node_list
match(node_list, mode) // compares items of node_list in a certain mode. Mode can be: nodes_and_attributes, nodes, attributes
get(node_list) // exits and returns nodes to client.
set(node_list1, node_list2) // will overwrite node_list1 with node_list2
union()
intersect()
subset()
difference()
authenticate()
remote_query(string)
settings:
set_type_conversion(object) // a type is read from a node when querried, and automatically converted based on a dictionary with the types as keys and the values as
conversion functions.
set_name_numbering(function_formula)
set_clone_name_to_group_conversion(boolean)
set_destination_namepath_symbol(string)
set_destination_grouppath_symbol(string)
set_endpoints(object) // object containing URL's with nicknames (the keys)
set_document_type(string) // two built-in API adaptors for: XML, JSON. Default is XML. To create your own API adaptor, you have to overwrite the public
members of sunaclpath.
set_document_source(document) // depending on the environment that sunaclpath is loaded in, the document source syntax may be different and needs to be set
explicitly. Default is a browser's "document" variable.
set_name_sync(attribute_name)
set_group_sync(attribute_name)
set_groups_sync(attribute_name)
set_grouppath_sync(attribute_name)
set_grouppaths_sync(attribute_name)
set_references_sync(attribute_name)
set_delegations_sync(attribute_name)
set_api_namespace(boolean) // Sets the "sunaclpath" namespace for objects that inherit public sunaclpath members (such as a node_list, and a DOM Node in a
browser environment). Using the sunaclpath API becomes: object.sunaclpath.last_node for example.
set_binding(selector, event_name, action) // event_name can be: "created", "inserted", "removed", "moved", "data_changed", "attribute_changed"
Sunaclpath selector_filter Methods:
new({
"fruit" : ()=> this.sunaclpath.remove_attributes("abc"),
20
"apple" : ()=> this.sunaclpath.set_names("hello")
})
And Runtime Methods without namespace.
set:
_____________________________________________
set(live('bowl/fruit/apple/crust')); // #bowl will be the root
set(live('bowl/fruit/apple/core')); // redundant to set #bowl, #fruit and #apple again, but works (will skip only those nodes)
_____________________________________________
set(live('bowl/fruit/apple'));
set(live('bowl/fruit/apple/crust'));
set(live('bowl/fruit/apple/core'));
_____________________________________________
set(live('bowl/fruit/apple/<crust><core>'));
_____________________________________________
_____________________________________________
buffer.set("item", new('$bowl/$fruit/$apple/<$crust><$core>'));
buffer.get("item").sunaclpath.generate_grouppaths()
set(live(''), buffer.get("item"));
_____________________________________________
set(live(''), new('$bowl/$fruit/$apple/<$crust><$core>').sunaclpath.generate_grouppaths())
get:
_____________________________________________
get('bowl/fruit') // gets two nodes by name as list1
_____________________________________________
get('bowl/fruit/*') // gets two nodes plus remaining descendants as list1
_____________________________________________
get('bowl/fruit/*/') // gets two nodes plus the next level (children) as list1
_____________________________________________
get('!bowl/fruit') // gets one node as list1
_____________________________________________
get('#bowl/fruit') // a destination namepath that gets one node as list1
(this?.attributes["softness"]?.nodeName?.split(this.nodeName)?.length > 1) && this.attributes["softness"].nodeValue > 50;
_____________________________________________
// Filling slots from a list
<template>
<template sunaclpath:name="hello_template">
<div sunaclpath:group="hello">
Hello,
<div sunaclpath:group="slot"></div>
speaking here.
</div>
</template>
<script>
for (v of sunaclpath.live(".fruit")){
let clone = sunaclpath.live("#hello_template/hello").cloneNode()
clone.live(".hello/slot").textContent = v.textContent
document.getRootNode().appendChild(clone)
}
</script>
<template sunaclpath:group="fruit">apple</template>
<template sunaclpath:group="fruit">pear</template>
<template sunaclpath:group="fruit">orange</template>
</template>
_____________________________________________
<template>
<div sunaclpath:template_type="repeat" sunaclpath:template_ref=".fruit" sunaclpath:name="hello">
Hello,
<div sunaclpath:template_ref=".fruit"></div>
speaking here.
</div>
<template sunaclpath:group="fruit">apple</template>
<template sunaclpath:group="fruit">pear</template>
<template sunaclpath:group="fruit">orange</template>
</template>
_____________________________________________
<template>
<div sunaclpath:name="hello">
Hello,
21
<div sunaclpath:template_ref="./fruit"></div>
speaking here.
</div>
<div sunaclpath:group="hello" sunaclpath:template_ref="#hello">
<div sunaclpath:name="fruit">apple</div>
</div>
<div sunaclpath:group="hello" sunaclpath:template_ref="#hello">
<div sunaclpath:name="fruit">pear</div>
</div>
<div sunaclpath:group="hello" sunaclpath:template_ref="#hello">
<div sunaclpath:name="fruit">orange</div>
</div>
</template>
_____________________________________________
<template>
<template sunaclpath:name="hello_template">
<div sunaclpath:group="hello">
Hello,
<div sunaclpath:group="slot"></div>
speaking here.
</div>
</template>
<script>
for (v of ["apple", "pear", "orange"]){
let clone = sunaclpath.live("#hello_template/hello").cloneNode()
clone.live(".hello/slot").textContent = v.textContent
document.getRootNode().appendChild(clone)
}
</script>
</template>
_____________________________________________
<template>
<template sunaclpath:name="hello_template">
<div sunaclpath:group="hello">
Hello,
<div sunaclpath:group="slot"></div>
speaking here.
</div>
</template>
<script>
for (v of ["apple", "pear", "orange"]){
let clone = sunaclpath.live("#hello_template/hello").cloneNode()
clone.live(".hello/slot").textContent = v.textContent
document.getRootNode().appendChild(clone)
}
</script>
</template>
22
2.4.3
A tiny bit of implementation code
/*
su-name path and group separator suggestions:
╱
/ ︽
ǀ
꘡
ノ
ᚋ
ހ
ᐅ ᐳ ᗒ ᗆ ᗚ
su-name selector prefix suggestions:
ⵀ ⵌ ↂ ⴲ
This initialization_script provides a better way to retrieve elements in your User Script through semi-unique names (su-names).
Mijnkatgoriezoektviz uses su-names in your document to create a pleasantly navigable Workspace. So if you made a document with
Mijnkatgoriezoektviz,
it is only natural to continue using that naming system in your User Scripts. To that end, this script modifies the way in which you can
use document.getElementById in your User Scripts. Using document.getElementById still requires a unique id argument, but the id is a path to your
element
consisting of su-names separated by forward slashes. Each element has a su-name of your choice. The path has to be unique,
but the benefit is that it doesn't pollute the "id namespace" as much as a unique variable name.
For example, you have the following SVG document:
<svg mkgzv:name = "animals">
<g mkgzv:name = "rabbit">
<rect mkgzv:name = "head" width = "20" height = "20"/>
</g>
<g mkgzv:name = "rat">
<rect mkgzv:name = "head" width = "10" height = "10"/>
</g>
</svg>
Then in your User Script you can use: document.getElementById("animals/rabbit/head") to get the "head" element of the "rabbit" element.
You won't have to worry about name clashes with the "head" in "rat" because it isn't in the directory "rabbit".
The su-name has to be in the mkgzv namespace and as an attribute it is written as mkgzv:name.
If you decide to use su-names in your drawing, don't mix them with XML/HTML id's. Id's (and thus paths) are generated from su-names by this
initialization_script.
Setting SVG and HTML attributes that expect id's also works. For example, <use xlink:href = "#animals/rabbit/head" />.
The rules for su-names are like in certain file systems:
1) has to be unique in its parent directory.
*/
/*
This version of CEF and Firefox 70.0.1 (32-bit) have difficulty with namespaces that are added through their DOM editors.
The namespaces disappear in Firefox and CEF in the code, but are accessible by scripts. Editing a namespace in code works once,
but the prefix and localname are then converted to a single attribute without namespace (ns:b is the attribute for example).
Don't use their DOM editors. Trust Javascript.
In the mkgzv object there are some polyfills that have equivalent behaviour to certain DOM methods, but with the addition of
working with the su-name system: element_append_child is the same as DOM's element.appendChild, but in addition the element's
descendants id's are updated. That's because the appending of a child to some element usually means that the child came from some
other parent, or it was an orphan, and it was relocated. Thus it and its descendants must get a new path.
element_get_name and element_set_name get and set the su-name of an element.
Mkgzv's public methods were created because while the DOM's setAttribute, getAttribute and appendChild methods were sufficient to
trigger a mutation observer handler, the mutation observer handler runs asynchronously in relation to the point at which it was
triggered. For example, element.appendChild triggered a mutation observer handler correctly, but any code after element.appendChild
would run before the handler was finished.
*/
namespace = "sunamepath"
node_inserted_and_name_attribute_observer_init = {attributes: true, childList: true, subtree: true}
// Sets the su-name of an element and updates it and its descendants id's.
function element_set_name(element, name){
node_inserted_and_name_attribute_observer.disconnect()
if (element.getAttributeNS(namespace, 'name') == null){
name_attribute = document.createAttributeNS(namespace, "name")
element.setAttributeNode(name_attribute)
}
element.setAttributeNS(namespace, "name", name)
update_element_descendants_ids(element)
node_inserted_and_name_attribute_observer.observe(document.getElementById("drawing"), node_inserted_and_name_attribute_observer_init)
//alert("element_set_name")
}
function document_create_element_ns(namespace, element_name, name, parent = null){
23
let element = document.createElementNS(namespace, element_name)
if (parent){
parent.appendChild(element)
}
element_set_name(element, name)
return element
}
function element_get_name(element){
return element.getAttributeNS(namespace, "name")
}
function element_append_child(element, child){
node_inserted_and_name_attribute_observer.disconnect()
element.appendChild(child)
update_element_descendants_ids(child)
node_inserted_and_name_attribute_observer.observe(document.getElementById("drawing"), node_inserted_and_name_attribute_observer_init)
}
// This function updates an element's descendants' id's and the element's id as a path.
function update_element_descendants_ids(node, modify_root = true){
//alert("update element id's")
if (modify_root == true){
if (node.parentNode != null){
node.setAttributeNS(null, "id", node.parentNode.getAttributeNS(null, "id") + "/" + node.getAttributeNS(namespace, "name"))
}
}
for (descendant_node of node.getElementsByTagName("*")){
descendant_node.setAttributeNS(null, "id", descendant_node.parentNode.getAttributeNS(null, "id") + "/" +
descendant_node.getAttributeNS(namespace, "name"))
}
}
// This function checks for element hierarchy (parent) changes in the DOM and then runs update_element_descendants_ids on the document
fragment which consists of the element's descendants.
// The document fragment descendants' id's are thus updated when the document fragment has moved in the DOM or has been created.
function node_inserted_and_name_attribute_handler(mutation_record, observer){
if (mutation_record[0].type == "attributes"){ // node is an attribute
// This version of CEF doesn't support MutationObserverInit.attributeFilter with custom namespaces. Thus, all attributes changes are being
listened for and the selection of su-name is done below.
if (mutation_record[0].attributeName == "name" && mutation_record[0].attributeNamespace == namespace){
update_element_descendants_ids(mutation_record[0].target)
}
} else if(mutation_record[0].type == "childList"){
for (node of mutation_record[0].addedNodes){
if (node.nodeType == Node.ELEMENT_NODE){ // node is an element
update_element_descendants_ids(node)
}
}
}
}
document.documentElement.setAttributeNS(null, "id", document.documentElement.getAttributeNS(namespace, "name"))
// This mutation observer is used to detect manual editing of the DOM
node_inserted_and_name_attribute_observer = new MutationObserver(node_inserted_and_name_attribute_handler)
node_inserted_and_name_attribute_observer.observe(document.getElementById("drawing"), node_inserted_and_name_attribute_observer_init)
// When document has loaded, run update_element_descendants_ids once so that all elements get an id.
update_element_descendants_ids(document.getElementById("drawing"), false)
24
2.5
Website
’
Restaurant
Delicaato’
Delicaato’
(HTML,
CSS,
Javascript,
Javascript,
NodeJS,
NodeJS,
Express)
Express)
This is a project I did with a class member of Utrecht University, first year, class Webtechnology. It
is a mockup
webshop
for buying ice cream. We were given 10 weeks. Our subtasks used HTML,
CSS and Javascript; front and back-end. Server: Node.js Express.
Database: SQL-Lite.
Some of the features I implemented were
basic
account
login security:
input sanitization,
anti-
session hijacking,
anti-
SQL injection; the wrapper methods for the database, which maps JSON
HTTP body parameters to SQLite commands, does error handling, SQL sanitization, and converts
to JSON for use client-side;
most of the basket and food items GUI and its programming, and I
drew most of textures you can see in the video; an HTML import function, and some of the CSS.
2.5.1
Demonstration video
25
Video loading stopped.
Video playback aborted due to a network error.
Video can’t be played because the file is corrupt.
Video format or MIME type is not supported.
No video with supported format and MIME type found.
Video playback aborted due to an unknown error.
This video is playing in Picture-in-Picture mode.
Pop out this video
More screens are more fun. Play this video while you do other things.
Loading:
0:00
/ 0:00
Off
2.5.2
Database wrapper code
26
2.6
Advanced
Advanced
keyboard
keyboard
functions
functions
language
language
This is a Python class that allows advanced keyboard functionality in a browser. It interprets a list of
application action/function names associated with a programmable functionality in a notation such
as ["Shift^", "Shift<", "Control?", "CapsLock?"]. The notation is a set of conditions that allows a
user to activate some keyboard function with mandatory and optional keys, by the un-pressing of
keys, the repetition of keys, the pressing of certain past keys before the active keyboard function
and the pressing of a certain past key before the last active key. Here is the class:
def powerset(input_set, output_sub_set = [], first_run = True, include_input_set_element = True, pointer_index = 0):
global output_set
if first_run:
output_set = []
if len(input_set) == 0:
return output_set
else:
if include_input_set_element:
output_sub_set.append(input_set[pointer_index])
if pointer_index >= len(input_set) - 1:
output_set.append(output_sub_set[:])
if include_input_set_element:
output_sub_set.pop()
return
pointer_index += 1
powerset(input_set, output_sub_set, False, True, pointer_index)
powerset(input_set, output_sub_set, False, False, pointer_index)
if include_input_set_element and not first_run:
output_sub_set.pop()
return first_run and output_set
class keyboard():
event = __new__(KeyboardEvent("keydown"))
lock_keys = ["CapsLock", "ScrollLock", "NumLock"]
maximum_key_alternatives = 2
maximum_active_keys = 5
keyboard_functions = {"" : {"main_svg" : "no_key"}}
keyboard_keys_singular_distribution = {}
pressed_keyboard_keys = {}
pressed_keyboard_keys_list = []
active_keyboard_keys = []
keyboard_keys_states = []
keyboard_keys_states_dict = {}
windows_keyboard_functions = {}
keyboard_keys_past_states = {
"keys_up" : [],
"any_state" : {"copyable" : {"keys_same_container" : {}, "keys" : {}, "counters" : {"start_count" : 0, "end_count" : -1}}, "keys_amounts" : {},
"keys_amount_cyclic_counts" : {}, "keys_list" : [], "keys_list_only_state" : []},
"past_singular_keys_from_keyboard_functions" : {"count" : 0},
"previous_keydown_active_keyboard_functions" : [],
"previous_keydown_active_keyboard_function" : "",
"last_keydown_active_keyboard_function" : "",
"previous_non_repeating_keydown_active_keyboard_function" : "",
"keydown_alternation" : False
}
active_keyboard_function = ""
active_keyboard_function_repeat = False
def __init__(self):
window.file_read("resources/application/user_defined_settings.json", "application_files", "user_defined_settings", self.user_defined_settings_loaded_handler)
#self.user_defined_settings_loaded_handler(open("resources/application/user_defined_settings.json", "r").read())
def user_defined_settings_loaded_handler(self, file_content):
#file_deserialized = JSON.parse(file_content.replace(r"\n",""))
file_deserialized = JSON.parse(file_content)
# reversing the keyboard_functions key-values
cleaner = "__cleaner__('change_for_loop_of_to_in')"
for function_index in file_deserialized["keyboard_functions"]:
function_item = file_deserialized["keyboard_functions"][function_index]
function_item_results = self.parse_keyboard_function(function_item)
print("function_item_results: " + str(function_item_results))
for function_item_result in function_item_results:
window_id = None
try:
window_id = function_index[:function_index.find("__")]
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
27
windows[window_id]["keyboard_events"]
except:
print("mkgzv_out: Couldn't recognize a valid window id within the keyboard function index. Skipping this keyboard function.")
continue
try:
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
self.keyboard_functions[function_item_result["function_item_string"]]
except: self.keyboard_functions[function_item_result["function_item_string"]] = {}
self.keyboard_functions[function_item_result["function_item_string"]][window_id] = {"keyboard_function" : function_index, "conditional_tokens" :
function_item_result["conditional_tokens"], "global_tokens" : function_item_result["global_tokens"]}
def update_active_keyboard_function(self, keyboard_event): # keyboard function that is active from key combinations currently being pressed or unpressed
self.event = keyboard_event
keyboard_key_with_state_token = keyboard_event.type == "keydown" and keyboard_event.key or keyboard_event.key + "^"
past_conditional = True
#print("keyboard_singular_distribution: " + str(self.keyboard_keys_singular_distribution))
try:
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
self.keyboard_keys_singular_distribution[keyboard_key_with_state_token]
past_singular_keys_from_keyboard_functions = self.keyboard_keys_past_states["past_singular_keys_from_keyboard_functions"]
past_singular_keys_from_keyboard_functions[keyboard_key_with_state_token] = {"count" : past_singular_keys_from_keyboard_functions["count"],
"past_keyboard_keys_states" : copy.deepcopy(self.keyboard_keys_past_states["any_state"]["copyable"])}
past_singular_keys_from_keyboard_functions["count"] += 1
except: pass
# updating keyboard key states
try:
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
self.keyboard_keys_past_states["any_state"]["keys_amount_cyclic_counts"][keyboard_key_with_state_token]
self.keyboard_keys_past_states["any_state"]["keys_amount_cyclic_counts"][keyboard_key_with_state_token] += 1
except: self.keyboard_keys_past_states["any_state"]["keys_amount_cyclic_counts"][keyboard_key_with_state_token] = 1
if len(self.keyboard_keys_past_states["any_state"]["copyable"]["keys"]) >= self.maximum_active_keys:
self.keyboard_keys_past_states["any_state"]["keys_amounts"][self.keyboard_keys_past_states["any_state"]["keys_list_only_state"][0]] -= 1
if self.keyboard_keys_past_states["any_state"]["keys_amount_cyclic_counts"][keyboard_key_with_state_token] >= self.maximum_active_keys:
self.keyboard_keys_past_states["any_state"]["keys_amount_cyclic_counts"][keyboard_key_with_state_token] = 1
if (self.keyboard_keys_past_states["any_state"]["keys_amounts"][self.keyboard_keys_past_states["any_state"]["keys_list_only_state"][0]] <= 0) and
(self.keyboard_keys_past_states["any_state"]["keys_list_only_state"][0] != keyboard_key_with_state_token):
del(self.keyboard_keys_past_states["any_state"]["keys_amounts"][self.keyboard_keys_past_states["any_state"]["keys_list_only_state"][0]])
del(self.keyboard_keys_past_states["any_state"]["keys_amount_cyclic_counts"][self.keyboard_keys_past_states["any_state"]["keys_list_only_state"][0]])
del(self.keyboard_keys_past_states["any_state"]["copyable"]["keys"][self.keyboard_keys_past_states["any_state"]["keys_list"][0]])
del(self.keyboard_keys_past_states["any_state"]["copyable"]["keys_same_container"][self.keyboard_keys_past_states["any_state"]["keys_list_only_state"]
[0]]["keys_same"][self.keyboard_keys_past_states["any_state"]["keys_list"][0]])
if len(self.keyboard_keys_past_states["any_state"]["copyable"]["keys_same_container"][self.keyboard_keys_past_states["any_state"]["keys_list_only_state"]
[0]]["keys_same"]) == 0:
del(self.keyboard_keys_past_states["any_state"]["copyable"]["keys_same_container"][self.keyboard_keys_past_states["any_state"]["keys_list_only_state"]
[0]])
cleaner = "__cleaner__('replace_array_delete_with_splice')"
del(self.keyboard_keys_past_states["any_state"]["keys_list"][0])
cleaner = "__cleaner__('replace_array_delete_with_splice')"
del(self.keyboard_keys_past_states["any_state"]["keys_list_only_state"][0])
self.keyboard_keys_past_states["any_state"]["copyable"]["counters"]["start_count"] += 1
try:
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
self.keyboard_keys_past_states["any_state"]["keys_amounts"][keyboard_key_with_state_token]
self.keyboard_keys_past_states["any_state"]["keys_amounts"][keyboard_key_with_state_token] += 1
except: self.keyboard_keys_past_states["any_state"]["keys_amounts"][keyboard_key_with_state_token] = 1
self.keyboard_keys_past_states["any_state"]["copyable"]["counters"]["end_count"] += 1
key_serial_number = self.keyboard_keys_past_states["any_state"]["keys_amount_cyclic_counts"][keyboard_key_with_state_token]
keyboard_key_with_state_token_serialized = keyboard_key_with_state_token + str(key_serial_number)
self.keyboard_keys_past_states["any_state"]["copyable"]["keys"][keyboard_key_with_state_token_serialized] = {"count" :
self.keyboard_keys_past_states["any_state"]["copyable"]["counters"]["end_count"]}
self.keyboard_keys_past_states["any_state"]["keys_list"].append(keyboard_key_with_state_token_serialized)
self.keyboard_keys_past_states["any_state"]["keys_list_only_state"].append(keyboard_key_with_state_token)
try:
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
self.keyboard_keys_past_states["any_state"]["copyable"]["keys_same_container"][keyboard_key_with_state_token]
except: self.keyboard_keys_past_states["any_state"]["copyable"]["keys_same_container"][keyboard_key_with_state_token] = {"keys_same" : {}, "last_key" :
""}
self.keyboard_keys_past_states["any_state"]["copyable"]["keys_same_container"][keyboard_key_with_state_token]["keys_same"]
[keyboard_key_with_state_token_serialized] = self.keyboard_keys_past_states["any_state"]["copyable"]["keys"][keyboard_key_with_state_token_serialized]
self.keyboard_keys_past_states["any_state"]["copyable"]["keys_same_container"][keyboard_key_with_state_token]["last_key"] =
keyboard_key_with_state_token_serialized
if keyboard_event.type == "keydown":
if keyboard_event.key in self.lock_keys:
try:
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
self.pressed_keyboard_keys[keyboard_event.key]
del(self.pressed_keyboard_keys[keyboard_event.key])
except:
self.pressed_keyboard_keys[keyboard_event.key] = True
else:
self.pressed_keyboard_keys[keyboard_event.key] = True
elif keyboard_event.type == "keyup":
28
self.keyboard_keys_past_states["keys_up"].append(keyboard_key_with_state_token)
if len(self.keyboard_keys_past_states["keys_up"]) > self.maximum_active_keys:
cleaner = "__cleaner__('replace_array_delete_with_splice')"
del(self.keyboard_keys_past_states["keys_up"][0])
if keyboard_event.key not in self.lock_keys:
try:
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
self.pressed_keyboard_keys[keyboard_event.key]
del(self.pressed_keyboard_keys[keyboard_event.key])
except: pass # this is for when a window loses focus and regains it while in a key up state from the previous window.
#print("PASS")
#print("keyboard_event.type: " + keyboard_event.type)
#print("self.keyboard_keys_past_states['keys_up']: " + str(self.keyboard_keys_past_states["keys_up"]))
#print("self.pressed_keyboard_keys: " + str(self.pressed_keyboard_keys))
# potential_function_item_result is a string combination of active up and down keys, starting with the maximum_active_keys amount of up/down keys. It is used
as an index to self.keyboard_functions to check it's existance as a keyboard function. The matching process: one up key at a time is added (starting with the latest) and it
is checked again for a match until a final check when there's only down keys left.
potential_function_item_results = {}
potential_function_item_results_as_lists = {}
potential_function_item_results_start_index = 0
self.active_keyboard_keys = []
self.keyboard_keys_states = []
self.pressed_keyboard_keys_list = list(self.pressed_keyboard_keys.keys())
pressed_keyboard_keys_length = len(self.pressed_keyboard_keys)
active_keyboard_function_dict = None
active_keyboard_function_found = False
for keyboard_key_down in self.pressed_keyboard_keys_list:
self.keyboard_keys_states.append(keyboard_key_down)
for keyboard_key_up_index in range(len(self.keyboard_keys_past_states["keys_up"])):
if len(self.pressed_keyboard_keys) + keyboard_key_up_index + 1 > self.maximum_active_keys: break
keyboard_key_up = self.keyboard_keys_past_states["keys_up"][keyboard_key_up_index]
potential_function_item_results_start_index = len(self.keyboard_keys_past_states["keys_up"]) - keyboard_key_up_index - 1
self.keyboard_keys_states.append(keyboard_key_up)
cleaner = "__cleaner__('list_variables_concatenation_make_type_explicit')"
potential_function_item_result = (self.pressed_keyboard_keys_list + self.keyboard_keys_past_states["keys_up"]
[potential_function_item_results_start_index:])
potential_function_item_result.sort()
potential_function_item_results_as_lists[potential_function_item_results_start_index] = potential_function_item_result
potential_function_item_results[potential_function_item_results_start_index] = "".join(potential_function_item_result)
if len(self.pressed_keyboard_keys) > 0:
self.pressed_keyboard_keys_list.sort()
potential_function_item_results[len(self.keyboard_keys_past_states["keys_up"])] = "".join(self.pressed_keyboard_keys_list)
potential_function_item_results_as_lists[len(self.keyboard_keys_past_states["keys_up"])] = self.pressed_keyboard_keys_list
for potential_function_item_result_index in range(potential_function_item_results_start_index, potential_function_item_results_start_index +
len(potential_function_item_results)):
potential_function_item_result = potential_function_item_results[potential_function_item_result_index]
try:
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
self.keyboard_functions[potential_function_item_result][window.window_focus]
active_keyboard_function_dict = self.keyboard_functions[potential_function_item_result][window.window_focus]
self.active_keyboard_keys = potential_function_item_results_as_lists[potential_function_item_result_index]
active_keyboard_function_found = True
break
except: pass
#print("self.keyboard_keys_states: " + str(self.keyboard_keys_states))
#print("self.active_keyboard_keys: " + str(self.active_keyboard_keys))
#print("potential_function_item_results: " + str(potential_function_item_results))
#print("self.keyboard_keys_past_states['any_state']: " + str(self.keyboard_keys_past_states["any_state"]))
if active_keyboard_function_found:
self.active_keyboard_function = active_keyboard_function_dict["keyboard_function"]
if len(active_keyboard_function_dict["conditional_tokens"]) > 0:
first_active_key = None
last_active_key = None
past_singular_key_count_minimum = float("inf")
past_singular_key_count_maximum = 0
for active_key in self.active_keyboard_keys:
past_singular_keys_from_keyboard_functions = self.keyboard_keys_past_states["past_singular_keys_from_keyboard_functions"]
if past_singular_keys_from_keyboard_functions["count"] < past_singular_key_count_minimum:
try: # if first_active_key is not found, it means that past_singular_keys_from_keyboard_functions contains keys states from one or more keyboard
functions ago. In that case, use a more recent key state.
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
past_singular_keys_from_keyboard_functions[active_key]
first_active_key = past_singular_keys_from_keyboard_functions[active_key]
past_singular_key_count_minimum = past_singular_keys_from_keyboard_functions["count"]
except: pass
if past_singular_keys_from_keyboard_functions["count"] >= past_singular_key_count_maximum:
try:
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
past_singular_keys_from_keyboard_functions[active_key]
last_active_key = past_singular_keys_from_keyboard_functions[active_key]
past_singular_key_count_maximum = past_singular_keys_from_keyboard_functions["count"]
29
except: pass
self.keyboard_keys_past_states["past_singular_keys_from_keyboard_functions"] = {"count" : 0}
active_key = None
keys_corresponding_to_past_conditional_tokens_consecutivity = {}
cleaner = "__cleaner__('change_for_loop_of_to_in')"
for key_corresponding_to_conditional_tokens in active_keyboard_function_dict["conditional_tokens"]:
# First check to see wether the keys corresponding to conditional tokens occured in the past. If one of the keys didn't occur in the past, the test fails.
conditional_tokens = active_keyboard_function_dict["conditional_tokens"][key_corresponding_to_conditional_tokens]
for conditional_token in conditional_tokens:
if conditional_token in ["«", "<"] and past_conditional:
active_key = conditional_token == "<" and last_active_key or first_active_key
print("first_active_key: " + str(first_active_key))
print("last_active_key: " + str(last_active_key))
try:
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
active_key["past_keyboard_keys_states"]["keys_same_container"][key_corresponding_to_conditional_tokens]["keys_same"]
[active_key["past_keyboard_keys_states"]["keys_same_container"][key_corresponding_to_conditional_tokens]["last_key"]]
past_key_any_state = active_key["past_keyboard_keys_states"]["keys_same_container"][key_corresponding_to_conditional_tokens]
["keys_same"][active_key["past_keyboard_keys_states"]["keys_same_container"][key_corresponding_to_conditional_tokens]["last_key"]]
keys_corresponding_to_past_conditional_tokens_consecutivity[past_key_any_state["count"]] = True
except:
past_conditional = False
# Second check to see wether the key states corresponding to conditional tokens occured consecutively, from the latest keys past states to the oldest.
#print("keys_corresponding_to_past_conditional_tokens_consecutivity: " + str(keys_corresponding_to_past_conditional_tokens_consecutivity))
current_key_corresponding_to_conditional_token_count = active_key["past_keyboard_keys_states"]["counters"]["end_count"]
cleaner = "__cleaner__('change_for_loop_of_to_in')"
for past_key_any_state in keys_corresponding_to_past_conditional_tokens_consecutivity:
try:
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
keys_corresponding_to_past_conditional_tokens_consecutivity[current_key_corresponding_to_conditional_token_count]
current_key_corresponding_to_conditional_token_count -= 1
except:
past_conditional = False
break
if not past_conditional: self.active_keyboard_function = ""
for global_token in active_keyboard_function_dict["global_tokens"]:
if global_token == "*":
self.active_keyboard_function_repeat = True
else:
self.active_keyboard_function = ""
#print("self.active_keyboard_function: " + self.active_keyboard_function)
"""
# may be useful in the future
if keyboard_event.type == "keydown":
self.keyboard_keys_past_states["keydown_alternation"] = not self.keyboard_keys_past_states["keydown_alternation"]
self.keyboard_keys_past_states["previous_keydown_active_keyboard_functions"].append(self.active_keyboard_function)
self.keyboard_keys_past_states["last_keydown_active_keyboard_function"] = self.active_keyboard_function
if len(self.keyboard_keys_past_states["previous_keydown_active_keyboard_functions"]) >= 2:
self.keyboard_keys_past_states["previous_keydown_active_keyboard_function"] =
self.keyboard_keys_past_states["previous_keydown_active_keyboard_functions"][0]
del(self.keyboard_keys_past_states["previous_keydown_active_keyboard_functions"][0])
if self.keyboard_keys_past_states["previous_keydown_active_keyboard_function"] != self.active_keyboard_function:
self.keyboard_keys_past_states["previous_non_repeating_keydown_active_keyboard_function"] =
self.keyboard_keys_past_states["previous_keydown_active_keyboard_function"]"""
def run_active_keyboard_function(self, keyboard_event):
if not keyboard_event.repeat2:
self.active_keyboard_function_repeat = False
self.update_active_keyboard_function(keyboard_event)
if (self.active_keyboard_function_repeat or not keyboard_event.repeat2) and self.active_keyboard_function != "":
self.windows_keyboard_functions[window.window_focus][self.active_keyboard_function]()
def update_keyboard_functions_in_settings(self): # update of the keyboard settings file
pass
def generate_keys_powerset(self, keys):
keys_optional = []
keys_required = []
function_item_result_string = ""
function_item_results = []
function_item_result_list = []
key_count = 0
print("keys: " + str(keys))
for key in keys:
if key.find("?") > -1:
key = key.replace("?","")
keys_optional.append(key)
30
else:
keys_required.append(key)
keys_optional_powerset = powerset(keys_optional)
for keys_optional_subset in keys_optional_powerset:
cleaner = "__cleaner__('list_variables_concatenation_make_type_explicit')"
function_item_result_list = keys_optional_subset + keys_required
function_item_result_list.sort()
function_item_result_string = "".join(function_item_result_list)
function_item_results.append(function_item_result_string)
return function_item_results
def parse_keyboard_function(self, function_item):
keys_alternatives = {}
keys_single = {}
keys_item_counts = {} # like a timer that counts, where each digit has its own counting range.
global_tokens = []
keys_alternatives_global_tokens = {}
print("function_item: " + str(function_item))
# removing the global token if found
for keys_item_count in range(len(function_item)):
keys_item = function_item[keys_item_count]
print("debug: " + keys_item)
if type(keys_item) == str:
# Brython's re module is pretty broken, so here's some hacks.
keys_item_is_global_token_item = regex.findall("^\W", keys_item)
try:
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
keys_item_is_global_token_item[0]
global_tokens = regex.findall("[\*\^]", keys_item)
cleaner = "__cleaner__('replace_array_delete_with_splice')"
del(function_item[keys_item_count])
break
except: pass
function_item_length = len(function_item)
for keys_index in range(function_item_length):
if function_item_length > self.maximum_active_keys:
print("mkgzv_out: skipping remaining keys from key states array because the maximum_active_keys is exceeded")
break
keys_item = function_item[keys_index]
if type(keys_item) == list:
if len(keys_item) > self.maximum_key_alternatives:
print("mkgzv_out: skipping remaining keys from key alternatives array because the maximum_key_alternatives is exceeded")
continue
keys_alternatives[keys_index] = keys_item
keys_item_counts[keys_index] = 0
# Removing the keys alternative global tokens
# Brython's re module is pretty broken, so here's some hacks.
for key in keys_item:
keys_alternatives_potential_global_tokens = regex.findall(r"^\W[\?]", key)
if len(keys_alternatives_potential_global_tokens) > 0:
keys_alternatives_global_tokens[keys_index] = keys_alternatives_potential_global_tokens
break
elif type(keys_item) == str:
keys_single[keys_index] = keys_item
else:
print("mkgzv_out: skipping keyboard key because it is not a string or array of strings or it's a global token in user_defined_settings.json")
if len(keys_alternatives) == 0:
function_item_results = []
function_item_result_list = []
conditional_tokens = {}
function_item_has_optional_token = False
for keys_single_value in keys_single.values():
function_item_result_list_value = keys_single_value
function_item_result_list_value_only_key_state_token = function_item_result_list_value.replace("?","").replace("«","").replace("<","")
function_item_result_list_value_potentially_with_optional_token = function_item_result_list_value.replace("«","").replace("<","")
if function_item_result_list_value.find("?") > -1: function_item_has_optional_token = True
if (function_item_result_list_value.find("«") > -1):
conditional_tokens[function_item_result_list_value_only_key_state_token] = ["«"]
elif (function_item_result_list_value.find("<") > -1):
conditional_tokens[function_item_result_list_value_only_key_state_token] = ["<"]
else:
self.keyboard_keys_singular_distribution[function_item_result_list_value_only_key_state_token] = True
function_item_result_list.append(function_item_result_list_value_potentially_with_optional_token)
if function_item_has_optional_token:
for function_item_result in self.generate_keys_powerset(function_item_result_list):
function_item_results.append({"function_item_string" : function_item_result, "conditional_tokens" : conditional_tokens, "global_tokens" :
global_tokens})
else:
function_item_result_list.sort()
function_item_result_string = "".join(function_item_result_list)
function_item_results.append({"function_item_string" : function_item_result_string, "conditional_tokens" : conditional_tokens, "global_tokens" :
global_tokens})
31
return function_item_results
function_item_results = []
while True:
carry_over = False
keys_alternatives_count = 0
cleaner = "__cleaner__('change_for_loop_of_to_in')"
for keys_alternatives_index in keys_alternatives:
function_item_result_list = []
conditional_tokens = {}
keys_item = keys_alternatives[keys_alternatives_index]
function_item_has_optional_token = False
function_item_result_list_value = keys_item[keys_item_counts[keys_alternatives_index]]
function_item_result_list.append(function_item_result_list_value)
cleaner = "__cleaner__('change_for_loop_of_to_in')"
for keys_single_index in keys_single:
function_item_result_list.append(keys_single[keys_single_index])
for function_item_result_list_index in range(len(function_item_result_list)):
function_item_result_list_value = function_item_result_list[function_item_result_list_index]
function_item_result_list_value_potentially_with_optional_token = function_item_result_list_value
function_item_result_list_value_only_key_state_token = function_item_result_list_value.replace("?","").replace("«","").replace("<","")
try:
cleaner = "__cleaner__('throw_exception_when_expression_undefined')"
keys_alternatives_global_tokens[keys_alternatives_index]
for keys_alternative_global_token in keys_alternatives_global_tokens[keys_alternatives_index]:
if keys_alternative_global_token == "?":
function_item_has_optional_token = True
function_item_result_list_value_potentially_with_optional_token += "?"
except: pass
if (function_item_result_list_value.find("«") > -1):
conditional_tokens[function_item_result_list_value_only_key_state_token] = ["«"]
elif (function_item_result_list_value.find("<") > -1):
conditional_tokens[function_item_result_list_value_only_key_state_token] = ["<"]
else:
self.keyboard_keys_singular_distribution[function_item_result_list_value_only_key_state_token] = True
function_item_result_list[function_item_result_list_index] = function_item_result_list_value_potentially_with_optional_token
if function_item_has_optional_token:
for function_item_result in self.generate_keys_powerset(function_item_result_list):
function_item_results.append({"function_item_string" : function_item_result, "conditional_tokens" : conditional_tokens, "global_tokens" :
global_tokens})
else:
function_item_result_list.sort()
function_item_result_string = "".join(function_item_result_list)
function_item_results.append({"function_item_string" : function_item_result_string, "conditional_tokens" : conditional_tokens, "global_tokens" :
global_tokens})
if keys_item_counts[keys_alternatives_index] >= (len(keys_item) - 1):
keys_item_counts[keys_alternatives_index] = 0
carry_over = True
else:
keys_item_counts[keys_alternatives_index] += 1
carry_over = False
if carry_over:
if keys_alternatives_count >= (len(keys_alternatives) - 1):
return function_item_results
else:
break
keys_alternatives_count += 1
32
2.7
Python
Regexes
Regexes
that
fix
Transcrypt
Transcrypt
bugs
(pre/post-processor)
(pre/post-processor)
Transcrypt is a Python to Javascript transpiler. It has some bugs, so I made some regexes to fix the
input Python and output Javascript code so that a browser understands the output. For instance,
sorting wouldn't work with Python behaviour. There's also some Regexes that modify HTML/SVG
tags due to some bugs in CEF. This a small part of a larger script that detects HTML/SVG script
tags and hands them over to the transpiler. I don't use an existing Python XML parsing library
because a Regex-based solution can fix bugs in Python, Javascript and HTML. I think Regex is very
powerful.
2.7.1
Small part of code
if settings["output_format"] == "xhtml":
GUI_transpiled_string = regex.sub(r"<\s*/\s*svg\s*>\s*$", "", GUI_transpiled_string)
elif settings["output_format"] == "html":
# used to detect and remove CDATA, and to add closing tags to self closing html tags (which isn't allowed).
Every script tag in the GUI (svg, html) has CDATA because inkscape deletes illegal characters if it is not wrapped in
CDATA. This script removes html CDATA because html doesn't allow that. XHTML is not exactly the same as HTML
and the GUI scripts rely on that, so we can't use that.
GUI_transpiled_string = regex.sub(r"<\s*html\s*:(?P<part1>\s*(?P<tag_name>[^\s]+)[^>]+>)\s*<!\[CDATA\[(?
P<text>.*?)\]\]>\s*<\s*/\s*html\s*:\s*(?P=tag_name)\s*>", r"<\g<part1>\g<text></\g<tag_name>>",
GUI_transpiled_string, flags = regex.DOTALL) # remove CDATA from html nodes
html_opening_tag_pattern = r"<\s*html\s*:(?P<part1>\s*(?P<tag_name>[^\s]+).*?)/\s*>"
GUI_transpiled_string = regex.sub(html_opening_tag_pattern, r"<\g<part1>></\g<tag_name>>",
GUI_transpiled_string, flags = regex.DOTALL) # add a closing tag if the html tag is self-closing
def GUI_replace_script_with_transpiled(match):
global javascript_packages
print("transpiling script...")
GUI_script_for_transpilation_object = open(GUI_transpiled_scripts_path + "GUI_script_for_transpilation.py",
"w")
# Pre-processing that corrects the mistakes of Transcrypt
indent_level = regex.match(r"(\s*)", match.group("content")).group(1)
GUI_script_for_transpilation = regex.sub(r"^" + indent_level, "", match.group("content"), flags =
regex.MULTILINE)
GUI_script_for_transpilation = regex.sub(r"(?P<part1>[^.])\b(?P<keys>keys)\b", "\g<part1>_keys",
GUI_script_for_transpilation)
get_recursive_structure_components_instance = string_manipulation.get_recursive_structure_components()
def delete_list_item(match):
delete_list_components = get_recursive_structure_components_instance(match.group("list"), r"\[", r"\]",
components_output = ["body_recursive"])
delete_list_item_index_dict = delete_list_components[delete_list_components["children_count"]]
delete_list_item_index = delete_list_item_index_dict["body_recursive"]
delete_list = match.group("list")[:delete_list_item_index_dict["body_start"]]
return match.group("indent") + delete_list + ".splice(" + delete_list_item_index[1:-1] + ",1)"
GUI_script_for_transpilation = regex.sub(r"""^(?P<indent>\s*)(?P<cleaner>(var\s*)?
cleaner\s*=\s*\"__cleaner__\('replace_array_delete_with_splice'\)["])$\s+del\s*\(\s*?(?P<list>.+?)\s*\)\s*$""",
delete_list_item, GUI_script_for_transpilation, flags = regex.MULTILINE)
GUI_script_for_transpilation_object.write(GUI_script_for_transpilation)
GUI_script_for_transpilation_object.close()
subprocess.call([sys.path[0].replace("\\", "/") + "\\" + GUI_transpiled_scripts_path +
"transpile_GUI_script_to_javascript.bat"])
GUI_script_transpiled_object = open(javascript_packages_transcrypt_generation_path +
"GUI_script_for_transpilation.js", "r")
GUI_script_transpiled = GUI_script_transpiled_object.read()
# After-processing that corrects the mistakes of Transcrypt
33
def javascript_import_replace(match):
if match.group("module").find("resources/backend") > -1: # only the Transcrypt modules have the wrong path
due to the transpiler module path flags not working.
return match.captures()
else:
return match.group("part1") + absolute_file_path_to_relative(javascript_packages_GUI_scripts_path,
javascript_packages_transcrypt_path + match.group("module")) + match.group("quote")
GUI_script_transpiled = regex.sub(javascript_import_pattern, javascript_import_replace, GUI_script_transpiled)
GUI_script_transpiled = regex.sub(r"""^(?P<cleaner>.*?"__cleaner__\('change_for_loop_of_to_in'\)".*?$)\s(?
P<for>\s*for\s+.+\s+)(?P<of>of)(?P<iterator>\s+.+?\s*\{.*?$)""", r"\g<for>in\g<iterator>", GUI_script_transpiled,
flags = regex.MULTILINE) #__cleaner__('change_for_loop_of_to_in')
GUI_script_transpiled = regex.sub(r"""^(?P<cleaner>.*?"__cleaner__\
('list_variables_concatenation_make_type_explicit'\)".*?$)\s(?P<variable_left>[^=]*)=(?P<variable_right1>[^+]+)\+(?
P<variable_right2>.+?)($|;)""", r"\g<variable_left> = \g<variable_right1>.concat(\g<variable_right2>);",
GUI_script_transpiled, flags = regex.MULTILINE) #__cleaner__('list_variables_concatenation_make_type_explicit')
#GUI_script_transpiled = regex.sub(r"\(function\s*\(\s*\)\s*\{\s*var\s*__accu[^_]*__\s*=\s*(?P<variable>[^;$]
+)[^}]*?[.]py_sort[^}]*\}\s*\)\s*\(\s*\);?$", r"\g<variable>.sort(function(a,b){return a>b})", GUI_script_transpiled,
flags = regex.MULTILINE | regex.DOTALL) # fix for Python's .sort() when the -o transpiler flag is used.
GUI_script_transpiled = regex.sub(r"""^(?P<cleaner>.*?"__cleaner__\('list_numbers_sort'\)".*?$)\s(?
P<indent_and_list>\s*.*?)[.]py_sort\s*\(\s*\)""", r"\g<indent_and_list>.sort(function(a,b){return a>b})",
GUI_script_transpiled, flags = regex.MULTILINE | regex.DOTALL) #__cleaner__('list_numbers_sort'): fix for
Python's .sort() when the -o transpiler flag is absent.
GUI_script_transpiled = regex.sub(r"""^(?P<cleaner>.*?"__cleaner__\
('throw_exception_when_expression_undefined'\)".*?$)\s(?P<indent>\s*)(?P<expression>[^$;]+)[$;]""",
r"\g<indent>if (!\g<expression>){throw ''};", GUI_script_transpiled, flags = regex.MULTILINE)
#__cleaner__('throw_exception_when_expression_undefined')
GUI_script_transpiled = regex.sub(r"""get\s+cleaner\s*\(\s*\)\s*\{return\s+cleaner;?\}\s*,\s*set\s+cleaner\s*\
(value\)\s*\{cleaner\s*=\s*value;?},?""", r"", GUI_script_transpiled, flags = regex.MULTILINE) # cleanup the
'cleanup' variables that serve no purpose
GUI_script_transpiled = regex.sub(r"""[.]py_metatype""", r".type", GUI_script_transpiled, flags =
regex.MULTILINE)
GUI_script_transpiled = regex.sub(r"""new\s*__call__\s*\((?P<argument1>[^,)]+)\s*,?[^,)]+(,\s*(?
P<argument3>[^)]+))?\)""", r"new \g<argument1>(\g<argument3>)", GUI_script_transpiled) # Fix for Transcrypt
__call__ function that errors when used with the Javascript "new" operator. You can't have commas and parenthesis in
your arguments because this is a simple fix.
34
2.8
svgwrite
svgwrite
svg
file
loader
I wrote a file loader for the Python svgwrite module. svgwrite makes manipulation and creation of
programmatically created svg files possible. Besides svgwrite's programmatic content creation, I
added the ability to load svg content from disk.
2.8.1
T
he script
from .. import svgwrite
import keyword
build_in_methods = ["id", "set", "get", "type"]
svg_text_object_names = ["text", "tspan", "textpath", "textarea"]
allowed_svg_namespaces = ["", "svg", "xlink"]
def load_svg(load_file_path, new_file_path = None, parent_to_load_into = None): # if parent_to_load_into is an element, the svg document
fragment will be put into that element. If none, the svg document fragment is put into the root svg. will be put into
ns_names_to_local_names =
{"http://www.w3.org/XML/1998/namespace"
: "xml"}
local_names_to_ns_names = {"xml" :
"http://www.w3.org/XML/1998/namespace"}
svg_xml_namespaces = svgwrite.etree.etree.iterparse(load_file_path, events=['start-ns'])
for namespace in svg_xml_namespaces:
ns_names_to_local_names[namespace[1][1]] = namespace[1][0]
local_names_to_ns_names[namespace[1][0]] = namespace[1][1]
svg_xml_tree = svgwrite.etree.etree.parse(load_file_path)
svg_xml_tree_root = svg_xml_tree.getroot()
for metadata_element in svg_xml_tree_root.findall("{" + local_names_to_ns_names[""] + "}metadata"):
svg_xml_tree_root.remove(metadata_element)
etree_parent_map = {child : parent for parent in svg_xml_tree_root.iter() for child in parent}
svg_xml_tree_descendants = [svg_xml_tree_root] + svg_xml_tree_root.findall(".//")
elements_etree_to_svgwrite = {}
if parent_to_load_into:
etree_parent_map[svg_xml_tree_root] = parent_to_load_into.get_xml()
child_parent_svgwrite = parent_to_load_into
#print(etree_parent_map)
for child in svg_xml_tree_descendants:
try: child_parent = etree_parent_map[child]
except: child_parent = None
child_parent_tag = None
child_grandparent_tag = None
try: child_grandparent = etree_parent_map[child_parent]
except: child_grandparent = None
try: child_tag = child.tag.split("}")[1]
except: child_tag = child.tag
if not svgwrite.validator2.get_validator("full").is_valid_elementname(child_tag) or child_tag == "metadata":
continue
try: child_parent_tag = child_parent.tag.split("}")[1]
except:
if child_parent: child_parent_tag = child_parent.tag
try: child_grandparent_tag = child_grandparent.tag.split("}")[1]
except:
if child_grandparent: child_grandparent_tag = child_grandparent.tag
child_keywords_reformatted = ""
#print(child_tag, child_parent_tag, child_grandparent_tag, "SPACE\n")
child_keywords = ""
if child_tag != "defs":
if child_tag in svg_text_object_names:
child_keywords_reformatted = 'text="""' + (child.text or "") + (child.tail or "") + '""",'
for child_attribute_qualified_name_etree_notation in child.keys():
child_attribute_name = child_attribute_qualified_name_etree_notation
child_attribute_ns_name = ""
child_attribute_local_name = ""
child_attribute_qualified_name = child_attribute_qualified_name_etree_notation
if child_attribute_qualified_name_etree_notation.find("}") >= 0: # if namespace prefix is present on the tag
child_attribute_qualified_name_etree_notation_list = child_attribute_qualified_name_etree_notation.split("}")
child_attribute_ns_name = child_attribute_qualified_name_etree_notation_list[0][1:]
child_attribute_name = child_attribute_qualified_name_etree_notation_list[1]
if child_attribute_ns_name != "":
child_attribute_local_name = ns_names_to_local_names[child_attribute_ns_name]
child_attribute_qualified_name = child_attribute_local_name + ":" + child_attribute_name
#print(child_attribute_qualified_name)
if (child_attribute_local_name in allowed_svg_namespaces) and
svgwrite.validator2.get_validator("full").is_valid_svg_attribute(child_tag, child_attribute_qualified_name): # ignore attribute if it isn't valid svg.
if not (child_tag == "text" and child_attribute_qualified_name in ["x", "y"]): # svgwrite text instantiation with x,y attributes as decimals
35
bug workaround
child_attribute_name_reformatted = child_attribute_name
if (child_attribute_name in keyword.kwlist) or (child_attribute_name in build_in_methods):
child_attribute_name_reformatted = child_attribute_name + "_"
child_attribute_name_reformatted = child_attribute_name_reformatted.replace("-", "_")
child_keywords_reformatted = child_keywords_reformatted + child_attribute_name_reformatted + '="""' +
child.get(child_attribute_qualified_name_etree_notation) + '""",'
child_keywords = child_keywords + '"' + child_attribute_qualified_name + '":"' +
child.get(child_attribute_qualified_name_etree_notation) + '",'
child_keywords = child_keywords[:-1]
child_keywords_reformatted = child_keywords_reformatted[:-1]
print(child_keywords)
elif child_parent_tag == 'svg':
elements_etree_to_svgwrite[child] = svgwrite.container.Defs()
continue
try: child_parent_svgwrite = elements_etree_to_svgwrite[child_parent]
except: pass
if child_tag == "title":
elements_etree_to_svgwrite[child_parent].set_desc(title = child.text)
continue
if child_tag == "desc":
elements_etree_to_svgwrite[child_parent].set_desc(desc = child.text)
continue
element_instancing_method = getattr(svgwrite.Drawing(), child_tag)
if child_tag == "svg" and not child_parent_tag and not parent_to_load_into:
element_instancing_method = svgwrite.Drawing
child_keywords_reformatted = child_keywords_reformatted + ',filename="""' + new_file_path + '"""'
#print(child_tag, child_parent_tag)
if child_grandparent_tag == "svg" and child_parent_tag == "defs":
child_parent_svgwrite = elements_etree_to_svgwrite[child_grandparent].defs
if child_tag == "defs" and child_parent_tag != "svg":
element_instancing_method = svgwrite.container.Defs
#print(child_keywords_reformatted)
exec(
"element_instance = element_instancing_method(" + child_keywords_reformatted + ")\n"
"elements_etree_to_svgwrite[child] = element_instance\n"
"if child_parent_svgwrite: child_parent_svgwrite.add(element_instance)\n"
"element_instance.update({" + child_keywords + "})")
# svgwrite and svg syntax aren't a 1:1 match, but some elements that are getting created based on xml input must receive certain default
attributes.
return elements_etree_to_svgwrite[svg_xml_tree_root]
#main_drawing = load_svg("GUI_copy.svg", "main_drawing.svg")
#main_drawing.save(pretty=True)
36
2.9
List
of
opinions
opinions
on
bad
parts
of
webtechnology
webtechnology
standards
standards
(XML,
HTML,
DOM)
The following are my opinions on the bad parts of webtechnologies, such as XML, HTML and
DOM. Some parts may be outdated.
- Web standard missing: declarative 3D shapes
- HTML embedded script can't run after content loaded ("defer")
- HTML Imports and XInclude is not supported in browsers.
- HTML Script element's variables leak to all scripts
- HTML Script element does not allow top-level await
- Setting a HTML Input type="range" value property via script updates the graphics, but does not run event
listeners
- DOM DocumentFragment's descendants can't have properties set before being parented, without losing
those properties.
- A server is needed for a local html file just to load module scripts (because it satisfies CORS)
- Styles can't be referenced from same document
- Scoped Custom Elements missing
- State Manager missing
- Two directional binding of two properties missing
- Declarative template language (possibly js template literals, but extended element iteration and recursion)
missing in HTML template
- document.querySelector and document.getElementById can't use namespaces when written in XHTML
- Trying to create Scoped Styles:
1) With declarative Shadow Root doesn't work well in Edge and not at all in Firefox. Slotted elements are
not detected by a shadow style sheet.
2) With style as a slottable, it reflects in shadow root, but has no effect
- Some elements can't contain children, and thus no scripts. They get kicked out of the element and made
into a sibling. As a result, for example, a script in an <input type="file"> can't access parentNode, if using
document.currentScript.parentNode
- Chromium-based browsers support File System Access API, but Firefox doesn't, so you have to resort to
older API's (which don't support much) or File and Directory Entries API, which is sandboxed
- "this" keyword refers to window in scripts
- Saving folders as zip is impossible natively as there's no zipper function
- HTML scripts created programmatically can't run
- Web Components must be defined in javascript, making it:
1) inaccessible to XML tree viewers
2) Verbose for small content, when setting shadow dom, template, etc. (and many of them)
- The namespace dash in webcomponents is not converted to an XML namespace (in XHTML, Custom
Elements need a html-namespace followed by a hyphen next to an XML namespace, which is redundant)
- Scripts in shadowRoots don't have access to document.currentScript
- Custom Elements observedAttributes runs before constructor, which means you can't define a list of valid
attributes in constructor, because it is used by observedAttributes.
- In XHTML, only html namespaced (possibly extended with is="") built-in and custom elements support
attachShadow.
- In XHTML, namespace names can't be declared once, at the top, and at mid-level refer to it via prefix, in
order to make all descendants have that as default namespace; now you must prefix every descendant, or
declare default namespace names for each tree in a document, which is too verbose.
- In XHTML, a child in a ancestor node with a namespace, can't inherit that namespace when using
document.createElement (but it can at parse time). You have to do manual tree traversal and provide the
namespace to document.createElementNS.
37
2.10
3D
affine
polygon
projection
engine
(love2d)
(love2d)
I made multiple 3D
affine
polygon
projection
engines
written in Lua
in the 2D engine called Löve.
They were made from scratch without any use of build-in graphics libraries and they run on the
processor. Calculations for per-pixel perspective projections, texture interpolations and file format
conversions were done by me. The only drawing functions that were used is one to set a pixel and
one to draw a triangle. My engine supports affine textures, meshes, global lighting and a few
primitives. (see:
https://www.youtube.com/watch?v=kXWboA5JVYI
)
2.10.1
Version 1
38
2.10.2
Version 2
39
2.11
3D
polygon
polygon
perspective
perspective
projection
projection
engine
(C#,
OpenTK,
OpenTK,
GLSL
)
This project was done during my time at Utrecht University, year 1,
assignment 2,
for the subject
Graphics. I did it alone. The graphics library used is OpenTK
and the shaders were written in
GLSL
.
I created some classes for animation (with relatively orientable and positionable coordinate
systems, and keyframes), object creation organization, and small abstractions on top of OpenTK’s
matrix operator combinations.
2.11.1
Video demonstration
40
Video loading stopped.
Video playback aborted due to a network error.
Video can’t be played because the file is corrupt.
Video format or MIME type is not supported.
No video with supported format and MIME type found.
Video playback aborted due to an unknown error.
This video is playing in Picture-in-Picture mode.
Pop out this video
More screens are more fun. Play this video while you do other things.
Loading:
0:00
/ 0:00
Off
2.12
3D
raycasting
raycasting
voxel
engine
(love2d)
(love2d)
I made a 3D voxel rendering engine in the 2D engine called Löve written in Lua. It was made from
scratch without any use of build-in graphics libraries and it runs on the processor. It puts a pixel on
the screen that corresponds to a voxel in the voxel grid. Multiple scanning rays are fired from the
camera and can hit a voxel in the voxel grid. The mesh in the picture is supposed to be a book.
41
2.13
3D
raycasting
polygon
engine
(C#,
OpenTK)
This project was done during my time at Utrecht University, year 1,
assignment 1,
for the subject
Graphics.
Rays are shot at primitives in the scene from the camera viewing plane and the rays are
tested for intersections with them. The primitives have colors and textures and respond to the
environment’s lighting. I also made a 2D ray map of the scene viewed from above.
I
worked on it
alone.
The program and readme
took me 3 weeks to make.
The graphics library used
is OpenTK;
modules: Matrix and Vector
.
The program does not use advanced graphics card draw or
library primitive intersection or lighting functions; only a rectangle draw function was provided, so
I had to program every scene to screen transformation, scene-primitive intersections and scene-
lighting influence.
There was an accompanying mathematics subject I had followed (projections,
equations for primitives, matrix operations, etc.).
2.13.1
Demonstration video
.
42
Video loading stopped.
Video playback aborted due to a network error.
Video can’t be played because the file is corrupt.
Video format or MIME type is not supported.
No video with supported format and MIME type found.
Video playback aborted due to an unknown error.
This video is playing in Picture-in-Picture mode.
Pop out this video
More screens are more fun. Play this video while you do other things.
Loading:
0:00
/ 0:00
Off
2.13.2
Demonstration images (higher resolution)
Rendering takes a long time for moderate resolution. That is why the video was slow. The pictures
are higher resolution but have had more time to render.
43
44
2.13.3
The code
(video)
Code was made in Visual Studio
45
Video loading stopped.
Video playback aborted due to a network error.
Video can’t be played because the file is corrupt.
Video format or MIME type is not supported.
No video with supported format and MIME type found.
Video playback aborted due to an unknown error.
This video is playing in Picture-in-Picture mode.
Pop out this video
More screens are more fun. Play this video while you do other things.
Loading:
0:00
/ 0:00
Off
2.13.4
Programming d
esign decisions and manual
From the readme.txt:
Assignment by: Arundel Gauthier
id: 1144278
Features:
- Camera:
- Notes:
- Camera pixels are different from Screen pixels.
- Camera x and y rotation components meaning: x: altitude, y: azimuth. z is unused.
- Controls:
- A, W, S, D is movement;
- mouse right click and drag is rotation.
- Q, E is up and down
- Z, X changes field of view
- Properties:
- field_of_view in degrees. Creates a zoom effect. Focal length changes, but plane remains a
square with range [-0.5, 0.5] on x and y dimensions.
- rotation_sensitivity: how many steps a mouse pixel drag can make it rotate.
- width, height. These are camera pixels. They map to screen pixels from window.
- screen_plane. Rays are fired through this from camera.position. This updates with camera rotation and translation.
- Omnidirectional light: instantiate a Light.Omnidirectional class with a position, intensity and color.
- Spotlight (bonus): instantiate a Light.Spot class
- Diffuse material: is controlled by setting a Primitive's color property, and the diffuse effect by a Light's
intensity and color property, and the distance between the light and primitive.
- Glossy material (Phong): is controlled by setting a Primitive's specular_color and specular_intensity
properties, and the specular effect by the Light's distance to the Primitive.
- Ambient light: can be controlled by setting Lighting.ambient_intensity and a Primitive's color.
- Spatial: a class
- Defines basic spatial properties such as position and rotation, but no dimensions.
- Primitive: a class
- Can be drawn (although Ray isn't, but in theory can be)
- Inherits from Spatial.
- Plane, Sphere and ray are in Primitive class.
- Member classes inherit from Primitive.
- Textures: Planes can have a texture. Controlled by a texture argument to a Plane's constructor.
- Debug: is a simplified top-view of the scene, with camera rays (primary and shadow), lines for planes, circles
for spheres and lights.
- key R down activates debug, R up deactivates it
- has a scale property
- Scene:
- Y-axis is up.
- Stores 2 types: User and Helper
- User is for what the user creates
- Helper is for annotations, light gizmos (if I were to have made that), GUI, etc. Can still be made of Primitives.
- Intersections: a class that stores rays that have intersected with scene objects.
- Window:
- Stretches camera pixels to fill screen, while preserving camera pixels aspect ratio.
- Attribution to:
- Template practical 1 from University Utrecht, subject Graphics
- Someone that made the stone_wall and grass texture
Any user instantiable class:
1) uses first capital letter per word and underscores between words.
46
2) is top-level or sometimes nested
Non-instantiable singletons (e.g.: Primitive) have the same notation as an instantiable class and can be nested.
When instanced, will throw an error. Static keyword is not always possible due to them being inheritees.
Some singletons (e.g. Camera) have to be instantiated once, because I could not find a way to have a static class inherit.
Some classes within classes are used for grouping and dot notation of nested objects and fields, which therefore:
1) have an underscore as the first character.
2) are replaced with their own instance when the outer class is instantiated. They can thus not be instantiated by
the user.
Namespaces can not solve the issue of having dot notation with each or one of the levels having inheritance capabilities, therefore
these different naming conventions for classes exist.
There are also nested classes that are not used for grouping of nested objects, but for their inheritance relation.
These nested classed can be instantiated with dot notation by the user (e.g.: Primitive.Sphere), and follow conventions of user
instantiable classes.
class-name(params Vector3[] _params): base(_params[0], _params[1]) { } --> This calls the super class constructor and passes
arguments from the subclass constructor call.
It is a pattern used frequently to make inheritance possible in constructor calls of derived classes.
The parameters are generically named because it is redundant to individualize such common, unaltered argument passing, for
several classes.
Some classes have update methods, aswell as some nested classes, such as Camera and Camera._Screen_Plane;
in this case the parent has a dependency on its child. Parent calls child's update.
Parent has an update method aswell, which is called by Raytracer.update, which in turn is called by
Raytracer.render and in turn Tick.
In Init objects are instantiated.
47
2.14
GUI
editor
plugin
(Roblox)
(Roblox)
I made an unfinished GUI editor plugin written in Lua for the 3D game creation platform called
Roblox. It can do similar tasks to a vector graphics drawing program.
2.14.1
The interface
48
2.14.2
An example of GUI's created with the editor
49
2.15
Model
Texture
Texture
Mapper
plugin
(Roblox)
(Roblox)
I made an unfinished Model Texture Mapper plugin for Roblox. It is supposed to enhance the
texturing abilities of Roblox. As it currently stands, Roblox can not apply a texture map to a model
consisting of many parts. It can only apply one texture at a time. There is an option in Roblox to
upload a mesh, basically a 3D model with texture maps. The downside of that is that the mesh can
not disintegrate (destroyed to pieces) because it is a whole. I am working on a solution that can
apply a texture map to each individual part of a model and therefore allowing a model to be blown
apart. There is not much to see result wise because the code is unfinished.
2.15.1
The interface
50
2.16
Camera
Character
Character
Collision
Collision
Physics
Physics
simulator
simulator
(Roblox)
(Roblox)
I made a Camera Character Collision Physics Simulator Plugin for Roblox.
This is a plugin that
simulates your player "first person mode" camera from play solo mode in studio mode. Roblox
doesn't provide collision physics for your camera when you are in studio mode (build mode). So I
scripted physics in Lua for the camera. This plugin allows you to walk, fly, and jump through your
level in studio mode, like your character does, but without an actual character! It provides collision
detection and response for your camera!
There is also gravity with a custom acceleration and
direction that acts on the camera. One difference with this plugin and play solo mode is that your
camera can climb walls!
2.16.1
The interface
51
2.17
Polygon
Polygon
to
voxel
converter
converter
(Roblox)
(Roblox)
I made a polygon to voxel terrain converter script in Lua. The script converts meshes that are in
the .mesh format to a lua voxel table grid format and the result is displayed on the screen. Polygon
vertices are given and the script interpolates between the vertices to get points on the corresponding
surfaces. (see:
https://www.youtube.com/watch?v=WlTHN-4jzDc
)
2.17.1
Some example conversions
52
2.18
Mesh
collision
collision
detection
detection
(Roblox)
(Roblox)
I made a Mesh Collision Detection script in Roblox in Lua. Roblox did not have collision detection
for meshes in the year 2013 so I made a script that could do that task. It works like this: polygon
mesh is given in the .mesh format and the mesh is converted to voxels and put into a voxel grid.
Once your character approaches a voxel, a primitive part is put at that position. The computation
time will always be limited by your chosen collision bounding volume no matter how many voxels
the mesh has. The RAM, however, can be enormous if you're not careful. (see:
https://www.youtube.com/watch?v=sQnUx7WUpjQ
)
2.18.1
Example of a collidable mesh
The blue blocks show the behind the scenes of the mesh collision detection mechanism. The blue
blocks are the voxels that are put in place when the character gets close to the mesh.
53
2.19
Scripted
Scripted
particle
particle
effects
effects
(Roblox)
(Roblox)
I scripted dynamic Particle Effects in Roblox in Lua around the time that Roblox had no such
advanced effects as displayed here. (see:
https://www.youtube.com/watch?v=nJ-PDMFVuK0
)
2.19.1
S
ome example
particles
54
2.20
Paint
(JavaScript,
(JavaScript,
HTML)
I made some barebone paint application in JavaScript and HTML.
2.21
Tesla
coil
turret
(Roblox)
(Roblox)
I made a Tesla Coil Turret model in Roblox that shoots bolts at zombies. It is scripted in Lua. (see:
https://www.youtube.com/watch?v=aQypW0mdlWw
)
55
2.22
First
person
shooter
shooter
demo
(Roblox)
(Roblox)
I made a basic first person shooter game in Roblox with weapon animations, an ammo box system
and an NPC boss with super powers in Lua. (see:
https://www.youtube.com/watch?
v=-8fA7XMxxmQ
)
2.23
Rock
jumper
game
(Roblox)
(Roblox)
A GUI rock jumper game in Roblox
programmed
in
Lua. The character and coins have basic
collision detection and animations.
56
2.24
Tic-tac-toe
Tic-tac-toe
(Roblox)
(Roblox)
A classic tic-tac-toe game made in Roblox with Lua.
2.25
Animation
Animation
tool
(Roblox)
(Roblox)
A very basic animation tool/plugin I made for Roblox in Lua. One brick at a time can be animated.
For instance, you use a block and move and rotate it with the numpad keys, and when you are done
with positioning and orienting the block you press the capture frame button. You repeat the process
to create a frame by frame animation (see:
https://www.youtube.com/watch?v=gsCxQ-y8Pzk
).
2.26
Build
tool
(Roblox)
(Roblox)
A build tool to build in-game in Roblox with some features that Roblox didn't have at the time, like
a brick rotating with your camera. It is unfinished. (see:
https://www.youtube.com/watch?
v=rvJF9cjOq7M
)
2.26.1
The interface
57
2.27
Minimap
Minimap
(Roblox)
(Roblox)
I made a dynamic GUI minimap script in Roblox in Lua. It uses raycasting by shooting rays from a
grid originating above your character. The minimap can display your surroundings (see:
https://www.youtube.com/watch?v=Mm-KFoRx0qc
)
2.28
Triangle
Triangle
Extrusion
Extrusion
Plugin
(Roblox)
(Roblox)
I made a Triangle Extrusion Plugin for Roblox. It is written in Lua. It can extrude an arbitrarily
oriented and flat 3D triangle so that it becomes a volumized triangle.
2.28.1
T
he interface
58
2.28.2
B
efore the extrusion
2.28.3
A
fter the extrusion
59
2.29
PAM
converter
converter
I made some functions that convert from the PAM image format family to a custom RGB image
format that can be read more easily by applications.
2.29.1
C
ode
function rgb_to_ppm_p6(str)
--[[
rgb string format will be as follows (with a space at the end of a line):
width height
0 0 0 233 231 241 0 3 56
12 32 11 44 22 10 12 0 250
]]
local result="P6\n"
local line_count=1
local value_count=1
local row_count=1
local image_file={{{}}}
local width,height=tostring(string.sub(str,1,string.find(str," ")-1)),tostring(string.sub(str,string.find(str," ")+1,string.find(str,string.char(10))-1))
local content=string.sub(str,string.find(str,string.char(10))+1,string.len(str))
result=result..width.." "..height.."\n".."255\n"
while content:len()>0 do
local first_char=string.sub(content,1,1)
if first_char==string.char(10) then
content=string.sub(content,2,string.len(content))
line_count=line_count+1
row_count=1
value_count=0
image_file[line_count]={{}}
end
local value=tostring(string.sub(content,1,string.find(content," ")-1))
result=result..string.char(value)
table.insert(image_file[line_count][row_count],value)
if value_count>=3 then
value_count=0
row_count=row_count+1
image_file[line_count][row_count]={}
end
content=string.sub(content,string.find(content," ")+1,string.len(content))
value_count=value_count+1
end
return result,image_file;
end
function pam_to_rgb(str)
local result=""
local row_count=1
local line_count=1
local tuple_count=0
local image_file={{{}}}
local width1,width2=string.find(str,"WIDTH ")
local content=string.sub(str,width2+1)
local width=tonumber(string.sub(content,1,string.find(content,"\n")-1))
local height1,height2=string.find(content,"HEIGHT ")
content=string.sub(content,height2+1)
local height=tonumber(string.sub(content,1,string.find(content,"\n")-1))
local depth1,depth2=string.find(content,"DEPTH ")
content=string.sub(content,depth2+1)
local depth=tonumber(string.sub(content,1,string.find(content,"\n")-1))
local header_end1,header_end2=string.find(content,"ENDHDR\n")
result=result..width.." "..height.." "..depth.."\n"
content=string.sub(content,header_end2+1)
image_file.width,image_file.height,image_file.depth=width,height,depth
for i=1,content:len() do
local value=tonumber(string.byte(string.sub(content,i,i)))
result=result..value.." "
tuple_count=tuple_count+1
if tuple_count>=depth then
tuple_count=0
row_count=row_count+1
60
image_file[line_count][row_count]={}
end
if row_count>width then
result=string.sub(result,1,result:len()-1).."\n"
row_count=1
tuple_count=0
line_count=line_count+1
image_file[line_count]={{}}
end
table.insert(image_file[line_count][row_count],value)
end
result=string.sub(result,1,result:len()-1)
return result,image_file;
end
61
2.30
Delta
robot
plays
tic-tac-toe
tic-tac-toe
This was a group project that lasted 11 weeks. I made a program in the Arduino C language that
could make a Delta Robot with a pen in its movable extender arm play tic-tac-toe against a person.
For this I had to make:
–
an image detection algorithm that could differentiate between hand drawn noughts, crosses
and empty fields.
–
A function that makes the Delta Robot move the pen in a cross and circle pattern.
–
A vector operations library.
–
A personality for the robot.
–
A function to convert a given position for the extender platform to the corresponding angles
of the servos.
–
A logarithmic formula to correct for the bad servo angle behaviour.
I will show my solution to a few problems, namely that of calculating the servo angles and the
vision algorithm for recognizing hand-drawn tic-tac-toe shapes.
2.30.1
Converting platform position to servo angles
Here is how I visualized the problem
(made with Roblox and PaintDotNET)
:
62
2.30.2
Code
Here is the code I made to do exactly this with vector trigonometry and 3D shape intersections
(I
created this analytic solution;
my
school had an iterative solution, which I ignored)
:
void get_servo_angles_from_position(float platform_position_x, float platform_position_y, float platform_position_z) {
struct vector platform_position;
platform_position.x = platform_position_x; platform_position.y = platform_position_y; platform_position.z = platform_position_z;
for (int servo_number = 0; servo_number < 3; servo_number++) {
// Calculate platform positions at the edge of the platform
struct vector servo_platform_position = vector_add(platform_position, vector_times_scalar(servo_platform_normals[servo_number],
platform_length));
struct vector servo_normal = servo_normals[servo_number];
struct vector servo_position = servo_positions[servo_number];
// Calculate sphere - plane intersection circle formula.
float servo_squared_normals = 1.0; //vector_components_add(vector_power(servo_normal, 2));
float servo_plane_constant_d = servo_plane_constants[servo_number];
float servo_sphere_plane_equation = vector_components_add(vector_times_vector(servo_normal, servo_platform_position)) +
servo_plane_constant_d;
float servo_circle_radius = sqrt(pow(top_arm_length, 2) - (pow(servo_sphere_plane_equation, 2) / servo_squared_normals));
struct vector servo_circle_position;
servo_circle_position.x = (servo_platform_position.x - ((servo_normal.x * servo_sphere_plane_equation) / servo_squared_normals));
servo_circle_position.y = (servo_platform_position.y - ((servo_normal.y * servo_sphere_plane_equation) / servo_squared_normals));
servo_circle_position.z = (servo_platform_position.z - ((servo_normal.z * servo_sphere_plane_equation) / servo_squared_normals));
// Find a point on the circle which distance to the servo position is equal to servo_arm_length by finding two circle intersection points. The first
circle has servo position as center and servo_arm_length as radius, the second circle is the intersection between the servo plane and the sphere with
center servo[number]_platform_position and radius top_arm_length
float servo_circle_distance = vector_magnitude(vector_subtract(servo_position, servo_circle_position));
float servo_circle_distance_squared = pow(servo_circle_distance, 2);
struct vector servo_circle_projected_position;
servo_circle_projected_position.x = sqrt(servo_circle_distance_squared - pow(servo_circle_position.y, 2));
servo_circle_projected_position.y = servo_circle_position.y;
servo_circle_projected_position.z = 0.0;
float servo_length_from_origin_perpendicular_to_intersection_points = (servo_arm_length_squared - pow(servo_circle_radius, 2) +
servo_circle_distance_squared) / (2.0 * servo_circle_distance);
struct vector servo_circle_centers_line_intersection_points_line_position =
vector_divides_scalar(vector_times_scalar(servo_circle_projected_position, servo_length_from_origin_perpendicular_to_intersection_points),
servo_circle_distance);
float servo_intersection_points_line_half_length = sqrt(servo_arm_length_squared -
pow(servo_length_from_origin_perpendicular_to_intersection_points, 2));
float servo_intersection_point_y_offset = servo_intersection_points_line_half_length * servo_circle_projected_position.x / servo_circle_distance;
float servo_intersection_point1_height = servo_circle_centers_line_intersection_points_line_position.y + servo_intersection_point_y_offset;
float servo_intersection_point2_height = servo_circle_centers_line_intersection_points_line_position.y - servo_intersection_point_y_offset;
float servo_angle = max(asin(servo_intersection_point1_height / servo_arm_length), asin(servo_intersection_point2_height / servo_arm_length));
my_servo[servo_number].angle = servo_angle;
}
}
63
2.31
Shape
recognition
recognition
algorithm
algorithm
(Translated from Dutch)
This algorithm can distinguish noughts, crosses and empty fields. It does so by scanning for open
line ends (nodes) through a pixel grid and by counting them. The pixel grid is aquired by scanning
the playing field with CNY reflection sensors. The image is then rasterized.
2.31.1
Surrounding pixels
The surrounding pixels of each pixel are analyzed. In order to understand the algorithm, several
random captures are shown from the process of scanning through the pixel grid. This means that
several random center pixels, chosen from random time steps in the program, are shown with their
surrounding pixels. For one center pixel, there are eight surrounding pixels. A capture is a 3x3 grid.
Figure A
2.31.2
Nodes
The algorithm searches for the amount of open
nodes
(line endings) in the pixel grid. The amount
of nodes are kept in a variable called
node_count
. The nodes are points which are surrounded by
one pixel in a 3x3 capture. If one node is found in a capture (for example in figure A at pixel 1),
then the center pixel may be marked as the line ending of a cross
or
a badly drawn open circle, but
it's not certain which one it is yet.
Figure B
2.31.3
Conditions for the type of shape
When one node is found, it is uncertain wether the shape is a circle, cross or an empty field. The
cross in figure A will not be recognized because it has malformed line endings. Only two nodes
were found: pixel 1 and 3. Pixel 7 is a weird case: for the eye it resembles the ending of cross' line,
but the algorithm is not advanced enough to detect it. For the shape to be a cross there must be
atleast 3 nodes
(in case of a slightly malformed cross) and a
maximum of 4 nodes
(in case of a
correct cross). A circle and an empty field don't have nodes. In order to distinguish a circle from an
empty field, a variable called
empty_threshold
is compared to the amount of filled pixels. If the
amount of filled pixels is above the threshold, then the field is
not empty and
the program has to
look further wether the shape could be a circle
or
cross. A circle has zero, one or two nodes.
64
2.32
Driving
Driving
robot
that
that
follows
follows
a
curve
&
removes
removes
obstacles
obstacles
This was a team project
(4 members)
that lasted around 11 weeks at HBO Electrical Engineering
year 1.
The objective was to add sensors,
actuators and a theme
to and program a
n existing
bare
wheeled-robot to follow a ground-placed curve
and remove obstacles
. The default curve detection
ability was handled by us assembling a fixed light-sensor array and a person writing an Arduino
program.
The end result:
One of my major hardware parts was a free-form extra credit
upgrade
to
the default
line detection
ability by creating a circuitboard that
controls
spinning sensors on a plate.
The sensor-array is
powered by wires that connect via sliding-contacts to the rotating wire disks.
I ran out of time to
finish it so the upgrade was not used.
It
and its schematic
(I made)
is shown
below
:
65
2.33
Driving
Driving
robot
that
detects
detects
a
laser
&
leader
robot
This was a team project that lasted 11 weeks
at HBO Electrical Engineering year 1
. I
created
a
system that makes a robot that is driving around detect a laser light, go towards the laser light.
It
uses an Arduino for the sensor readings and motor control.
The hardware I made consisted of a
surface of plexiglas and mirrors that is able to scatter light, and thus channel multi-directional
laserlight towards a few light capturing sensors. I also wrote the school report and did the
programming of the laser detection and navigation in C. When the robot ran into a laser at a random
steering angle, it could turn and drive towards the laser from 3 meters away in approximately 5
seconds.
2.33.1
The hardware
2.33.2
Operation scheme
Below is shown the scheme that shows how a certain version of the software calculated the angle:
66
2.34
Touchscreen
Touchscreen
Ship
Navigator
Navigator
This is a hardware & software system I worked on at my Heftronic internship that allows a user to
use a HMI panel (touch screen) to control the lighting on a ship with buttons and receive feedback
from the lighting and other sensors like battery voltage. There's menu's to configure the time, screen
brightness, alarms and ship name. There's the Navigation Page that displays the positions of the
lights within the ship on a map of the ship. The light activation buttons change color depending on
the feedback of the lights (green is on, red is off, green with yellow warning triangle means that the
on command was sent, but there was no feedback). This is explained in the legend menu. I made the
application in Javascript with Jmobile, which is an IDE that runs on the HMI panel. I interconnected
the hardware such as the relay modules (that control the lights) with the HMI panel by the RS485
bussystem (which used the Modbus RTU protocol). The RealTerm & PicoScope programs were
used to test and inspect the signals.
67
2.35
Radar
(Arduino
(Arduino
C,
Spyder
)
I made an electronic radar for a university project, class DATA-P, bachelor level,
for
three weeks.
The main task was to build a measurement device of choice, using an Arduino. The hand-ins were
Arduino tutorial exercises, a weekly statement, science report, the hardware and software. I chose to
work alone.
The radar must be moved by hand to scan the environment.
There are two input sensors: for
distance and rotation. The distance sensor at the end of the rotating arm could be chosen, and I
chose a sound-based one.
There are callibration and data recording hardware buttons.
The data is
transferred from an SD card to Spyder
IDE
, and plotted in there
by
a program I wrote.
2.35.1
Demonstration Video
68
Video loading stopped.
Video playback aborted due to a network error.
Video can’t be played because the file is corrupt.
Video format or MIME type is not supported.
No video with supported format and MIME type found.
Video playback aborted due to an unknown error.
This video is playing in Picture-in-Picture mode.
Pop out this video
More screens are more fun. Play this video while you do other things.
Loading:
0:00
/ 0:00
Off
2.35.2
Output Environment
Graph
Created by a Spyder script I wrote, using the radar SD card’s data
:
69
2.35.3
The data acquisition code
70
71
72
2.36
Quad
i
mage
to
square
function
function
I made an image to square function in Lua. It converts an arbitrarily quad shaped image (possibly an
image that was captured from a 3D scene under some camera angle) to a square image. Not all
necessary functions were included in this document to run the image_to_square function.
2.36.1
An example perspective conversion
2.36.2
The code
function linear_interpolate(y1,y2,mu)
return (y1*(1-mu)+y2*mu);
end
function interpolate_two_pixels(pixel1,pixel2,interpolation_factor)
local interpolated = {
number_limit(math.floor(linear_interpolate(pixel1.color[1],pixel2.color[1],interpolation_factor)),{min=0,max=255}),
number_limit(math.floor(linear_interpolate(pixel1.color[2],pixel2.color[2],interpolation_factor)),{min=0,max=255}),
number_limit(math.floor(linear_interpolate(pixel1.color[3],pixel2.color[3],interpolation_factor)),{min=0,max=255}),
}
if pixel1.color[4] and pixel2.color[4] then
table.insert(interpolated,number_limit(math.floor(linear_interpolate(pixel1.color[4],pixel2.color[4],interpolation_factor)),{min=0,max=255}))
end
return interpolated
end
function number_range(n,range)
local range_min_minus_one=(range.min-1)
local range_diff=range.max-range_min_minus_one
local val=n%math.abs(range_diff)
return ((val==0 and range_diff) or val)+range_min_minus_one
end
function map_range( a1, a2, b1, b2, s )
return b1 + (s-a1)*(b2-b1)/(a2-a1)
end
function CosineInterpolate(y1,y2,mu)
local mu2 = (1-math.cos(mu*math.pi))/2;
return (y1*(1-mu2)+y2*mu2);
end
function image_to_square(image_file,shape_coords,image_size)
local new_image_file={width=math.ceil(image_size.x),height=math.ceil(image_size.y),depth=image_file.depth}
local left_top_vertex=Vector2.new(math.floor(shape_coords[1].x),math.floor(shape_coords[1].y))
local right_top_vertex=Vector2.new(math.floor(shape_coords[2].x),math.floor(shape_coords[2].y))
local right_bottom_vertex=Vector2.new(math.floor(shape_coords[3].x),math.floor(shape_coords[3].y))
local left_bottom_vertex=Vector2.new(math.floor(shape_coords[4].x),math.floor(shape_coords[4].y))
--local right_bottom_top_vertex_difference=right_top_vertex-right_bottom_vertex
--right_top_vertex=right_top_vertex+right_bottom_top_vertex_difference.unit*(tonumber(image_size_x.Text) or 1)
--right_bottom_vertex=right_bottom_vertex+right_bottom_top_vertex_difference.unit*(tonumber(image_size_y.Text) or 1)
local length_line1=math.floor((left_bottom_vertex-left_top_vertex).magnitude)
local length_line2=math.floor((right_bottom_vertex-right_top_vertex).magnitude)
local longest_line=math.max(length_line1,length_line2)
local shortest_line=math.min(length_line1,length_line2)
73
local start_point_longest_line=((length_line1>length_line2) and left_top_vertex) or right_top_vertex
local end_point_longest_line=((length_line1>length_line2) and left_bottom_vertex) or right_bottom_vertex
local start_point_shortest_line=((length_line1<=length_line2) and left_top_vertex) or right_top_vertex
local end_point_shortest_line=((length_line1<=length_line2) and left_bottom_vertex) or right_bottom_vertex
for x=0,image_size.x-1 do
new_image_file[x]={}
end
for i_longest_line=0,longest_line-1 do
local y_longest_line=math.floor(map_range(0,longest_line-1,start_point_longest_line.y,end_point_longest_line.y,i_longest_line))
local x_longest_line=math.floor(map_range(0,longest_line-1,start_point_longest_line.x,end_point_longest_line.x,i_longest_line))
local i_shortest_line=math.floor(map_range(0,longest_line-1,0,shortest_line-1,i_longest_line))
local y_shortest_line=math.floor(map_range(0,shortest_line-1,start_point_shortest_line.y,end_point_shortest_line.y,i_shortest_line))
--local
y_shortest_line=math.floor(CosineInterpolate(start_point_shortest_line.y,end_point_shortest_line.y,map_range(0,shortest_line-1,tonumber(image_si
ze_x.Text),tonumber(image_size_y.Text),i_shortest_line)))
local x_shortest_line=math.floor(map_range(0,shortest_line-1,start_point_shortest_line.x,end_point_shortest_line.x,i_shortest_line))
--local
x_shortest_line=math.floor(CosineInterpolate(start_point_shortest_line.y,end_point_shortest_line.y,map_range(0,shortest_line-1,0,1,i_shortest_line)
))
local shortest_line_point,longest_line_point=Vector2.new(x_longest_line,y_longest_line),Vector2.new(x_shortest_line,y_shortest_line)
local length_horizontal_line=math.floor((shortest_line_point-longest_line_point).magnitude)
for i_horizontal_line=0,length_horizontal_line-1 do
local x_horizontal_line=map_range(0,length_horizontal_line-1,x_longest_line,x_shortest_line,i_horizontal_line)
local y_horizontal_line=map_range(0,length_horizontal_line-1,y_longest_line,y_shortest_line,i_horizontal_line)
local pixel=image_file[math.floor(x_horizontal_line)][math.floor(y_horizontal_line)]
local x_image=math.floor(map_range(0,length_horizontal_line-1,0,image_size.x-1,i_horizontal_line))
local y_image=math.floor(map_range(0,longest_line-1,0,image_size.y-1,i_longest_line))
new_image_file[x_image][y_image]=pixel
end
end
-- Start interpolation of rows
for y=0,new_image_file.height-1 do
local pixel_row={}
for x=0,new_image_file.width-1 do
if new_image_file[x] then
if new_image_file[x][y] then
table.insert(pixel_row,{position=Vector2.new(x,y),color=new_image_file[x][y]})
end
end
end
for i=1,#pixel_row do
if i>=2 then
local pixel1,pixel2=pixel_row[i-1],pixel_row[i]
local dist_two_pixels_x=pixel2.position.x-pixel1.position.x
for p=1,dist_two_pixels_x-1 do -- loop through empty space inbetween pixel1 and pixel2 and interpolate
local interpolation_factor=p/dist_two_pixels_x
if interpolation_modes[interpolation_mode]=="linear" then
new_image_file[pixel1.position.x+p][pixel1.position.y]=interpolate_two_pixels(pixel1,pixel2,interpolation_factor)
end
if interpolation_modes[interpolation_mode]=="nearest neighbor" then
new_image_file[pixel1.position.x+p][pixel1.position.y]=pixel1.color
end
end
end
end
end
74
-- Start interpolation of columns
for x=0,new_image_file.width-1 do
local pixel_column={}
for y=0,new_image_file.height-1 do
if new_image_file[x] then
if new_image_file[x][y] then
table.insert(pixel_column,{position=Vector2.new(x,y),color=new_image_file[x][y]})
end
end
end
for i=1,#pixel_column do
if i>=2 then
local pixel1,pixel2=pixel_column[i-1],pixel_column[i]
local dist_two_pixels_y=pixel2.position.y-pixel1.position.y
for p=1,dist_two_pixels_y-1 do -- loop through empty space inbetween pixel1 and pixel2 and interpolate
local interpolation_factor=p/dist_two_pixels_y
if interpolation_modes[interpolation_mode]=="linear" then
new_image_file[pixel1.position.x][pixel1.position.y+p]=interpolate_two_pixels(pixel1,pixel2,interpolation_factor)
end
if interpolation_modes[interpolation_mode]=="nearest neighbor" then
new_image_file[pixel1.position.x][pixel1.position.y+p]=pixel1.color
end
end
end
end
end
return new_image_file;
end
75
2.37
.mesh
to
.obj
converter
converter
I made a .mesh to .obj file format converter in Lua. These are file formats for 3D models.
-- It's best to run this .mesh to .obj converter code on a website with a Lua compiler/executer because you'll have a
higher chance that such compiler doesn't care about the string length. Roblox does care about the string length and this
code could error if your vertices string is too long.
-- This is a basic .mesh to .obj converter which converts vertex data, UV texture coords, and triangle normals. It does
NOT export: materials and other stuff.
local vertices="" -- Paste your .mesh file vertex data here
function MeshToObjFile(vertices)
local str=""
local vertex_data={}
local count,count2=0,0
for vertex in string.gmatch(vertices, "%[.-%]",1,true) do
count,count2=count+1,count2+1
local count2_div_three=count2/3
local ver, n = vertex:gsub("%[",""):gsub("%]","")
local v1=string.sub(ver,1,ver:find(",")-1)
ver=string.sub(ver,ver:find(",")+1,ver:len())
local v2=string.sub(ver,1,ver:find(",")-1)
ver=string.sub(ver,ver:find(",")+1,ver:len())
local v3=ver
vertex_data[#vertex_data+1]={v1,v2,v3}
if count==3 or count==6 or count==9 then
str=str.."v "..vertex_data[1][1].." "..vertex_data[1][2].." "..vertex_data[1][3].."\n"
str=str.."vt "..vertex_data[3][1].." "..vertex_data[3][2].."\n"
str=str.."vn "..vertex_data[2][1].." "..vertex_data[2][2].." "..vertex_data[2][3].."\n"
if count==9 then
str=str.."f "..(count2_div_three-2).."/"..(count2_div_three-2).."/"..(count2_div_three-2).." "..
(count2_div_three-1).."/"..(count2_div_three-1).."/"..(count2_div_three-1).."
"..count2_div_three.."/"..count2_div_three.."/"..count2_div_three.."\n"
count=0
end
vertex_data={}
end
end
return str;
end
print(MeshToObjFile(vertices))
-- Arundel
76
2.38
Factory
Factory
flow
monitor
monitor
(love2d)
(love2d)
I made an application demo with the Löve2d engine that monitors the product flow of factory
machinery (Factory Flow Monitor) real time. It aquires data in JSON format within UDP packets
from a Node Red UDP node. It uses LuaSocket to read the incoming UDP packets. The JSON data
is converted to a Lua table.
The incoming data can look like this:
{ "building_data":{"1":{"status":"running", "direction":"1"}, "2":{"status":"error", "direction":"1"},
"3":{"status":"stopped", "direction":"1"}}}
I made a GUI framework for the interface and its functionality like input text boxes, parenting,
windows, etc. which you can see in the pictures.
Buildings (the machines) are denoted by a square block. Buildings can be created and dragged onto
the playing field. The connections (lines) between buildings can be drawn by the user. Each
connection shows its status with a color that corresponds to one attached building. If a connection is
green, then the buildings to which the connection is attached are working properly. Red is an error,
white means stopped and black is for disconnected.
77
2.39
Some
Powerset
Powerset
Algorithms
Algorithms
Here I show a few powerset algorithms I made. One is in Python using recursion; the others in Lua
using a few custom algorithms.
2.39.1
In
Python
def power_set(input_set, output_sub_set = [], first_run = True, include_input_set_element = True, pointer_index = 0):
global output_set
if first_run:
output_set = []
if len(input_set) == 0:
return output_set
else:
if include_input_set_element:
output_sub_set.append(input_set[pointer_index])
if pointer_index >= len(input_set) - 1:
output_set.append(output_sub_set[:])
if include_input_set_element:
output_sub_set.pop()
return
pointer_index += 1
power_set(input_set, output_sub_set, False, True, pointer_index)
power_set(input_set, output_sub_set, False, False, pointer_index)
if include_input_set_element and not first_run:
output_sub_set.pop()
return first_run and output_set
power_set = power_set([1,2,3,4,5])
power_set.sort()
print(power_set)
78
2.39.2
In Lua
function love.load()
local benchmark_results = "input size, time1, time2, time3\n"
local set = {4,6,1,8,4,7,2,5,2,4,9,3,2}
local elapsed_times1 = {}
local elapsed_times2 = {}
local elapsed_times3 = {}
function generate_power_set1(set)
local unseparated_sub_sets = {}
local current_sub_sets = {{}}
local next_sub_sets = {}
for set_value_index = 1, #set do
current_sub_sets[1][set_value_index] = {value = set[set_value_index], value_length = tostring(set[set_value_index]):len()}
current_sub_sets[1][set_value_index].value_separation_pattern = current_sub_sets[1][set_value_index].value_length
--print("{"..set[set_value_index].."}")
end
while #current_sub_sets >= 1 do
local current_sub_set_length = 0
for sub_set_index = 1, #current_sub_sets do
local current_sub_set = current_sub_sets[sub_set_index]
current_sub_set_length = #current_sub_set
if current_sub_set_length > 2 then
table.insert(next_sub_sets, {})
end
for sub_sub_set_index = 1, current_sub_set_length - 1 do
local left_sub_sub_set = current_sub_set[sub_sub_set_index]
local right_sub_sub_set = current_sub_set[sub_sub_set_index + 1]
local value_last_item_length = right_sub_sub_set.value_separation_pattern % 100
local value_length = left_sub_sub_set.value_length + value_last_item_length
local value_last_item_template = 10 ^ value_last_item_length
local value_separation_pattern = left_sub_sub_set.value_separation_pattern * 100 + value_last_item_length
local value = left_sub_sub_set.value * value_last_item_template + right_sub_sub_set.value % value_last_item_template
local sub_sub_set = {value = value, value_length = value_length, value_separation_pattern = value_separation_pattern}
if current_sub_set_length > 2 then
next_sub_sets[#next_sub_sets][sub_sub_set_index] = sub_sub_set
end
table.insert(unseparated_sub_sets, sub_sub_set)
end
if current_sub_set_length > 2 then
repeat
local last_next_sub_set_index = #next_sub_sets
local last_next_sub_set = next_sub_sets[last_next_sub_set_index]
local last_next_sub_set_length = #last_next_sub_set
if last_next_sub_set_length > 2 then
table.insert(next_sub_sets, {})
end
for sub_sub_set_index = 1, last_next_sub_set_length - 1 do
local left_sub_sub_set = last_next_sub_set[sub_sub_set_index]
local right_sub_sub_set = last_next_sub_set[sub_sub_set_index + 1]
local value_last_item_length = right_sub_sub_set.value_separation_pattern % 100
local left_value_last_item_length = left_sub_sub_set.value_separation_pattern % 100
local value_all_items_except_last_length = left_sub_sub_set.value_length - left_value_last_item_length
local value_length = value_all_items_except_last_length + value_last_item_length
local value_last_item_template = 10 ^ value_last_item_length
local left_value_last_item_template = 10 ^ left_value_last_item_length
local value_separation_pattern = math.floor(left_sub_sub_set.value_separation_pattern / 100) * 100 + value_last_item_length
local value = math.floor(left_sub_sub_set.value / left_value_last_item_template) * value_last_item_template +
right_sub_sub_set.value % value_last_item_template
local sub_sub_set = {value = value, value_length = value_length, value_separation_pattern = value_separation_pattern}
if last_next_sub_set_length > 2 then
next_sub_sets[#next_sub_sets][sub_sub_set_index] = sub_sub_set
end
table.insert(unseparated_sub_sets, sub_sub_set)
end
until last_next_sub_set_length <= 2
end
end
current_sub_sets = next_sub_sets
79
next_sub_sets = {}
end
for sub_sub_set_index = 1, #unseparated_sub_sets do
local sub_sub_set_string = "{"
local sub_sub_set = unseparated_sub_sets[sub_sub_set_index]
local value = sub_sub_set.value
local value_separation_pattern = sub_sub_set.value_separation_pattern
repeat
local value_last_item_length = value_separation_pattern % 100
local value_last_item_template = 10 ^ value_last_item_length
local value_last_item = value % value_last_item_template
sub_sub_set_string = sub_sub_set_string..value_last_item..","
value_separation_pattern = math.floor(value_separation_pattern / 100)
value = math.floor(value / value_last_item_template)
until value_separation_pattern <= 0
sub_sub_set_string = sub_sub_set_string.."}"
--print(sub_sub_set_string)
end
end
function generate_power_set2(set)
local log_of_two = math.log(2)
local set_length = #set
for set_item_index = 1, set_length do
print("{"..set[set_item_index].."}")
end
for sub_set_index = 1, 2 ^ set_length - 1 do
local sub_set_count = sub_set_index
local sub_set_maximum_items = math.floor(math.log(sub_set_count) / log_of_two) + 1
local sub_set_string = "{"
for sub_set_item_index = 1, sub_set_maximum_items do
local sub_set_count_checked_bit = sub_set_count % 2
if sub_set_count_checked_bit == 1 then
sub_set_string = sub_set_string .. set[sub_set_item_index]..","
end
sub_set_count = math.floor(sub_set_count / 2)
end
sub_set_string = sub_set_string .. "}"
print(sub_set_string)
end
end
function generate_power_set3(s, start)
start = start or 1
if(start > #s) then return {{}} end
local ret = generate_power_set3(s, start + 1)
for i = 1, #ret do
ret[#ret + 1] = {s[start], unpack(ret[i])}
end
return ret
end
-- Benchmarking
for set_items = 1, #set do
local sub_set = {}
for set_item_index = 1, set_items do
table.insert(sub_set, set[set_item_index])
end
local best_elapsed_time1 = math.huge
local best_elapsed_time2 = math.huge
local best_elapsed_time3 = math.huge
for function_call_number = 1, 100 do
local start_time = love.timer.getTime()
generate_power_set1(sub_set)
local elapsed_time = love.timer.getTime() - start_time
80
if elapsed_time < best_elapsed_time1 then
best_elapsed_time1 = elapsed_time
end
start_time = love.timer.getTime()
generate_power_set2(sub_set)
elapsed_time = love.timer.getTime() - start_time
if elapsed_time < best_elapsed_time2 then
best_elapsed_time2 = elapsed_time
end
start_time = love.timer.getTime()
generate_power_set3(sub_set)
elapsed_time = love.timer.getTime() - start_time
if elapsed_time < best_elapsed_time3 then
best_elapsed_time3 = elapsed_time
end
end
table.insert(elapsed_times1, best_elapsed_time1)
table.insert(elapsed_times2, best_elapsed_time2)
table.insert(elapsed_times3, best_elapsed_time3)
benchmark_results = benchmark_results .. set_items .. "," .. best_elapsed_time1 .. "," .. best_elapsed_time2 .. "," .. best_elapsed_time3 .. "\n"
end
love.filesystem.write("benchmark_results.txt", benchmark_results)
end
function love.draw()
love.graphics.print("done",100,100)
end
2.39.3
How it works
generate_power_set1 and generate_power_set2 are algorithms I made. generate_power_set3 is an
algorithm I found on the internet used to compare against my algorithms. A benchmark test is
included. generate_power_set1 starts with an input set and then calculates the powerset by
calculating a few small permutations, and then bases larger permutations on the pattern of the
indices of the smaller permutations. Take a look at the following diagram:
81
2.40
Simple
Collision
Collision
Detection
Detection
Maze
(HTML/CSS/Javascript)
(HTML/CSS/Javascript)
Here is a program that has a randomly moving square that collides with walls of a maze. The maze
walls can be customized with CSS and HTML in a responsive way.
2.40.1
The code
<html>
<body>
<style>
:root {
--maze_wall_horizontal_opening_size: 100px;
--maze_wall_thickness: 10px;
}
* {
padding: 0px;
margin: 0px;
}
#manifestor_bar {
position:absolute;
width:40px;
height:40px;
left:15px;
top:15px;
background:blue;//rgba(0,0,0,0);
}
#goal {
position:relative;
width:40px;
height:40px;
left:calc(100% - 60px);
top:5px;
background:black;
}
#maze {
position:relative;
left: 0px;
top: 0px;
background: yellow;
width: 100%;
height: 500px;
}
.maze_wall_horizontal, .maze_wall_horizontal_left, .maze_wall_horizontal_right {
position: relative;
width: calc(100% - var(--maze_wall_horizontal_opening_size));
height: var(--maze_wall_thickness);
background: red;
}
.maze_wall_horizontal {
width: 100%;
}
.maze_wall_horizontal_right {
left: var(--maze_wall_horizontal_opening_size);
}
.maze_wall_horizontal_spacer {
position: relative;
width: 100%;
height: 50px;
background: rgba(0,0,0,0);
}
82
#maze_wall_vertical_left, #maze_wall_vertical_right {
position: absolute;
z-index: 1;
background: red;
height: 100%;
width: var(--maze_wall_thickness);
}
#maze_wall_vertical_right {
right: 0px;
}
.polygons {
fill:lime;
stroke:purple;
stroke-width:1;
}
</style>
<svg id="collision_tests" style="position:absolute;overflow:visible;z-index:10000" width="1000" height="1000">
<!--<polygon class="polygons" id="polygon1" points="0,0 50,0 50,900 0,900"></polygon>
<polygon class="polygons" id="polygon2" points="40,10 800,10 800,410 40,410"></polygon>-->
<circle id="plot_circle" r="2" fill="purple"></circle>
</svg>
<div id="maze" style="z-index:-1">
<div id="manifestor_bar"></div>
<div id="maze_wall_vertical_left" class="maze_wall"></div>
<div id="maze_wall_vertical_right" class="maze_wall"></div>
<div class="maze_wall maze_wall_vertical_right"></div>
<div class="maze_wall maze_wall_horizontal"></div>
<div class="maze_wall_horizontal_spacer"></div>
<div class="maze_wall maze_wall_horizontal_left"></div>
<div class="maze_wall_horizontal_spacer"></div>
<div class="maze_wall maze_wall_horizontal_right"></div>
<div class="maze_wall_horizontal_spacer">
<div id="goal"></div>
</div>
<div class="maze_wall maze_wall_horizontal"></div>
</div>
<script>
function clear_plot(){
var plot_circles = document.getElementsByClassName("plot_circle")
while (plot_circles.length !== 0){
plot_circles[0].remove()
}
}
function plot(x, y){
var point = document.getElementById("plot_circle").cloneNode()
document.getElementById("collision_tests").appendChild(point)
point.setAttribute("cx", x)
point.setAttribute("cy", y)
point.setAttribute("class", "plot_circle")
point.removeAttribute("id")
}
function point(x, y){
return {"x" : x, "y" : y}
}
function vector_angle(vertex){
var angle = Math.atan2(vertex.y, vertex.x)
return (angle < 0) && (angle + Math.PI * 2) || angle
}
function vector_translate(vertex1, vertex2){
return point(vertex1.x + vertex2.x, vertex1.y + vertex2.y)
}
function vectors_translate(vertices, translation){
var new_vertices = []
for (vertex of vertices){
new_vertices.push(vector_translate(vertex, translation))
}
return new_vertices
}
function vector_scale(vertex, scale){
return point(vertex.x * scale, vertex.y * scale)
}
function angle_ahead(angle1, angle2){
var two_pi = Math.PI * 2
var angles_increase = two_pi - angle1
angle2 = (angle2 + angles_increase) % two_pi
return angle2 > Math.PI
}
83
function magnitude(vertex1, vertex2){
var vertex2 = vertex2 || {"x" : 0, "y" : 0}
return Math.sqrt((vertex1.y - vertex2.y) ** 2 + (vertex1.x - vertex2.x) ** 2)
}
function dot_product(vertex1, vertex2){
return vertex1.x * vertex2.x + vertex1.y * vertex2.y
}
function point_line_signed_distance(line_vertex1, line_vertex2, vertex){
var line1 = vector_translate(vertex, vector_scale(line_vertex1, -1))
var line2 = vector_translate(line_vertex2, vector_scale(line_vertex1, -1))
var line1_dot_line2 = dot_product(line1, line2)
var line1_length = magnitude(line1)
var line2_length = magnitude(line2)
var line1_projected_on_line2_length = Math.abs(line1_dot_line2 / line2_length)
var line1_line2_angle = Math.acos(line1_projected_on_line2_length / line1_length)
var sign = angle_ahead(vector_angle(line2), vector_angle(line1)) && 1 || -1
var signed_distance = sign * line1_length * Math.sin(line1_line2_angle)
return signed_distance
}
function line_line_segment_collision_detect(line_segment1, line_segment2){
line_segment1 = [point(line_segment1[0].x, line_segment1[0].y), point(line_segment1[1].x, line_segment1[1].y)];
line_segment2 = [point(line_segment2[0].x, line_segment2[0].y), point(line_segment2[1].x, line_segment2[1].y)];
var line_segment1_gradient = (line_segment1[1].y - line_segment1[0].y) / (line_segment1[1].x - line_segment1[0].x)
var line_segment2_gradient = (line_segment2[1].y - line_segment2[0].y) / (line_segment2[1].x - line_segment2[0].x)
var line_segment1_y_intercept = line_segment1[0].y - (line_segment1_gradient * line_segment1[0].x)
var line_segment2_y_intercept = line_segment2[0].y - (line_segment2_gradient * line_segment2[0].x)
function sort_by_position(coordinate){
[line_segment1[0], line_segment1[1]] = [(line_segment1[0][coordinate] < line_segment1[1][coordinate]) && line_segment1[0] || line_segment1[1],
(line_segment1[0][coordinate] >= line_segment1[1][coordinate]) && line_segment1[0] || line_segment1[1]];
[line_segment2[0], line_segment2[1]] = [(line_segment2[0][coordinate] < line_segment2[1][coordinate]) && line_segment2[0] || line_segment2[1],
(line_segment2[0][coordinate] >= line_segment2[1][coordinate]) && line_segment2[0] || line_segment2[1]];
}
//console.log(line_segment1_gradient, line_segment2_gradient)
if (Math.abs(line_segment1_gradient) === Infinity){
sort_by_position("y")
var intersection_y = line_segment2_gradient * line_segment1[0].x + line_segment2_y_intercept
return (intersection_y > line_segment1[0].y) && (intersection_y < line_segment1[1].y) && (line_segment1[0].x > line_segment2[0].x) && (line_segment1[0].x <
line_segment2[1].x)
} else if (Math.abs(line_segment2_gradient) === Infinity){
sort_by_position("y")
var intersection_y = line_segment1_gradient * line_segment2[0].x + line_segment1_y_intercept
return (intersection_y > line_segment2[0].y) && (intersection_y < line_segment2[1].y) && (line_segment2[0].x > line_segment1[0].x) && (line_segment2[0].x <
line_segment1[1].x)
} else {
sort_by_position("x")
var intersection_x = (line_segment2_y_intercept - line_segment1_y_intercept) / (line_segment1_gradient - line_segment2_gradient)
return (intersection_x > line_segment1[0].x) && (intersection_x < line_segment1[1].x) && (intersection_x > line_segment2[0].x) && (intersection_x <
line_segment2[1].x)
}
}
function triangle_triangle_collision_detect(vertices1, vertices2){
// collision occurs when a vertex from vertices1 lies within triangle2 or when a vertex from vertices2 lies within triangle1, OR any of the edges of both triangles is
intersecting
var triangle1_edge1 = [vertices1[0], vertices1[1]], triangle1_edge2 = [vertices1[1], vertices1[2]], triangle1_edge3 = [vertices1[2], vertices1[0]]
var triangle2_edge1 = [vertices2[0], vertices2[1]], triangle2_edge2 = [vertices2[1], vertices2[2]], triangle2_edge3 = [vertices2[2], vertices2[0]]
for (vertex of vertices2){
if (point_line_signed_distance(triangle1_edge1[0], triangle1_edge1[1], vertex) > 0 && point_line_signed_distance(triangle1_edge2[0], triangle1_edge2[1], vertex)
> 0 && point_line_signed_distance(triangle1_edge3[0], triangle1_edge3[1], vertex) > 0){
return true
}
}
for (vertex of vertices1){
if (point_line_signed_distance(triangle2_edge1[0], triangle2_edge1[1], vertex) > 0 && point_line_signed_distance(triangle2_edge2[0], triangle2_edge2[1], vertex)
> 0 && point_line_signed_distance(triangle2_edge3[0], triangle2_edge3[1], vertex) > 0){
return true
}
}
return line_line_segment_collision_detect(triangle1_edge1, triangle2_edge1) || line_line_segment_collision_detect(triangle1_edge1, triangle2_edge2) ||
line_line_segment_collision_detect(triangle1_edge1, triangle2_edge3) ||
line_line_segment_collision_detect(triangle1_edge2, triangle2_edge1) || line_line_segment_collision_detect(triangle1_edge2, triangle2_edge2) ||
line_line_segment_collision_detect(triangle1_edge2, triangle2_edge3) ||
line_line_segment_collision_detect(triangle1_edge3, triangle2_edge1) || line_line_segment_collision_detect(triangle1_edge3, triangle2_edge2) ||
line_line_segment_collision_detect(triangle1_edge3, triangle2_edge3)
}
function get_element_vertices(element){
if (element instanceof HTMLElement){
var element_corner1 = point(element.getBoundingClientRect().left, element.getBoundingClientRect().top) // top-left corner
var element_corner2 = point(element.getBoundingClientRect().right, element.getBoundingClientRect().top) // continuing clock-wise
var element_corner3 = point(element.getBoundingClientRect().right, element.getBoundingClientRect().bottom)
var element_corner4 = point(element.getBoundingClientRect().left, element.getBoundingClientRect().bottom)
84
return [element_corner1, element_corner2, element_corner3, element_corner4]
} else if(element.tagName.toLowerCase() === "polygon"){
var vertices = []
for (vertex of element.points){
vertices.push(point(vertex.x, vertex.y))
}
return vertices
}
}
function triangularize_quad(vertices){
var triangle1 = [vertices[0], vertices[1], vertices[2]]
var triangle2 = [vertices[0], vertices[2], vertices[3]]
return [triangle1, triangle2]
}
function quad_quad_collision_detect(triangles1, triangles2){
// unfinished: doesn't work on obtuse angles and self-intersections
return triangle_triangle_collision_detect(triangles1[0], triangles2[0]) || triangle_triangle_collision_detect(triangles1[1], triangles2[0]) ||
triangle_triangle_collision_detect(triangles1[0], triangles2[1]) || triangle_triangle_collision_detect(triangles1[1], triangles2[1])
}
function quad_quad_collision_detect_with_overshoot(direction, delta, element1, element2){ // takes into account motion overshoot
var vertices1_old = get_element_vertices(element1)
var vertices2 = get_element_vertices(element2)
var triangles1_old = triangularize_quad(vertices1_old)
var triangles2 = triangularize_quad(vertices2)
var element1_old_element2_collision = quad_quad_collision_detect(triangles1_old, triangles2)
if (element1_old_element2_collision) { return true }
var delta2d = vector_scale(direction, delta)
var vertices1_new = vectors_translate(vertices1_old, delta2d)
var triangles1_new = triangularize_quad(vertices1_new)
//for (vertex of vertices1_new){
// plot(vertex.x, vertex.y)
//}
var element1_new_element2_collision = quad_quad_collision_detect(triangles1_new, triangles2)
if (element1_new_element2_collision) { return true }
var trajectory_quad1_element2_collision = quad_quad_collision_detect(triangularize_quad([vertices1_new[0], vertices1_old[0], vertices1_old[3],
vertices1_new[3]]), triangles2)
if (trajectory_quad1_element2_collision) { return true }
var trajectory_quad2_element2_collision = quad_quad_collision_detect(triangularize_quad([vertices1_new[0], vertices1_new[1], vertices1_old[1],
vertices1_old[0]]), triangles2)
if (trajectory_quad2_element2_collision) { return true }
var trajectory_quad3_element2_collision = quad_quad_collision_detect(triangularize_quad([vertices1_new[1], vertices1_old[1], vertices1_old[2],
vertices1_new[2]]), triangles2)
if (trajectory_quad3_element2_collision) { return true }
var trajectory_quad4_element2_collision = quad_quad_collision_detect(triangularize_quad([vertices1_new[3], vertices1_new[2], vertices1_old[2],
vertices1_old[3]]), triangles2)
if (trajectory_quad4_element2_collision) { return true }
return false
}
var maze_walls = document.getElementsByClassName("maze_wall")
/*if (quad_quad_collision_detect(triangularize_quad(document.getElementById("polygon1").points),
triangularize_quad(document.getElementById("polygon2").points))){
document.getElementById("polygon2").style.fill = "red"
}*/
/*if (quad_quad_collision_detect_with_overshoot(point(-1, 0), 10000, document.getElementById("polygon2"), document.getElementById("polygon1"))){
document.getElementById("polygon2").style.fill = "red"
}*/
/*setInterval(() => {
for (point of document.getElementById("polygon2").points){
point.x = Math.random() * 1000
point.y = Math.random() * 1000
}
if (quad_quad_collision_detect(triangularize_quad(document.getElementById("polygon1").points),
triangularize_quad(document.getElementById("polygon2").points))){
//if (triangle_triangle_collision_detect(get_element_vertices(document.getElementById("polygon1")),
get_element_vertices(document.getElementById("polygon2")))){
document.getElementById("polygon2").style.fill = "red"
} else {
document.getElementById("polygon2").style.fill = "lime"
}
}, 5000)*/
var scale = 100
var samples_amount = 10
85
console.log("START MANIFESTING!")
function frame(){
//clear_plot()
for (let axis = 0; axis < 2; axis++){
var walked_distance = 0
var positive_distance = 0
var negative_distance = 0
var delta = 0
for (let sample_count = 0; sample_count < samples_amount; sample_count++){
var number = Math.random() - 0.5
walked_distance += number
if (walked_distance > 0){
positive_distance += number
} else {
negative_distance += number
}
}
delta = (scale * (negative_distance + positive_distance))
var permission_to_move = true
for (maze_wall of maze_walls){
if (quad_quad_collision_detect_with_overshoot(point(axis === 0 && Math.sign(delta) || 0, axis === 1 && Math.sign(delta) || 0), Math.abs(delta),
document.getElementById("manifestor_bar"), maze_wall)){
permission_to_move = false
//console.log("collide")
break
}
}
if (quad_quad_collision_detect(triangularize_quad(get_element_vertices(document.getElementById("manifestor_bar"))),
triangularize_quad(get_element_vertices(document.getElementById("goal"))))){
document.getElementById("goal").style.visibility = "hidden"
}
if (permission_to_move){
document.getElementById("manifestor_bar").style[axis === 0 && "left" || "top"] = (document.getElementById("manifestor_bar").getBoundingClientRect()[axis
=== 0 && "x" || "y"] + delta) + "px"
}
}
requestAnimationFrame(frame)
}
requestAnimationFrame(frame)
</script>
</body>
</html>
86
3
Math & Physics
3.1
Understanding
Understanding
the
relation
relation
between
between
Cartesian
Cartesian
and
Polar
Fields
(
Application
Application
in
Desmos)
87
The rendering
3.2
Understanding
Understanding
Double
Integrals
Integrals
in
Polar
Coordinates
Coordinates
I attempted to understand double integrals in polar coordinates: the conversion factor
r
that appears
in the volume function
∫
∫
f
(
r
,
θ
)
⋅
r
dr
dθ
,
and why having
f
in polar coordinates is not enough.
through geometry (areas: ratio of circle sectors with rectangles, and their 3d extrusions). I have the
notes that lead to my understanding here (many of my conclusions were in my head, and they are
not written down, so I
have forgotten
them):
3.2.1
My application
in Geogebra
88
89
3.2.2
Code
in Javascript
To test my hypothesis’ of my understanding, I wrote some functions that perform double integration
with different methods.
function convert_to_polar(func){
return function(r, θ){ return func(r*Math.cos(θ), r*Math.sin(θ))}
}
function get_signed_area_function(func, a, b, iterations, independent_variable1){
return function calculate_signed_area(independent_variable2){
let area = 0
let domain = b - a
let step = domain / iterations
for (let i = 0; i < iterations; i++){
let iteration = step * i + a
let arg0 = independent_variable1 === 0 ? iteration : independent_variable2
let arg1 = independent_variable1 === 1 ? iteration : independent_variable2
area += func(arg0, arg1)*step
}
return area
}
}
function get_signed_area_function_polar4(func, a, b, iterations, independent_variable1){
return function calculate_signed_area(independent_variable2){
let area = 0
let domain = b - a
let step = domain / iterations
for (let i = 0; i < iterations; i++){
let iteration = step * i + a
let step_corrected = (a + b) / 2
let arg0 = independent_variable1 === 0 ? iteration : independent_variable2
let arg1 = independent_variable1 === 1 ? iteration : independent_variable2
area += func(arg0, arg1)*step
}
return area
}
}
// most accurate
function get_signed_area_function_polar1(func, a, b, iterations, independent_variable1){
return function calculate_signed_area(independent_variable2){
let area = 0
let domain = b - a
let step = (domain / iterations)
for (let i = 0; i < iterations; i++){
let iteration = step * i + a
let step_corrected = step*iteration + 0.5*step**2
let arg0 = independent_variable1 === 0 ? iteration : independent_variable2
let arg1 = independent_variable1 === 1 ? iteration : independent_variable2
area += func(arg0, arg1)*step_corrected
}
return area
}
}
function get_signed_area_function_polar2(func, a, b, iterations, independent_variable1){
return function calculate_signed_area(independent_variable2){
let area = 0
let domain = b - a
let step = (domain / iterations)
for (let i = 0; i < iterations; i++){
let iteration = step * i + a
let step_corrected = step*iteration //step + (step**2/(2*iteration))
let arg0 = independent_variable1 === 0 ? iteration : independent_variable2
let arg1 = independent_variable1 === 1 ? iteration : independent_variable2
area += func(arg0, arg1)*step_corrected
}
return area
}
}
//_________________________________________________________
90
// This function is written by ChatGPT based on my script
function get_signed_area_function_polar3(func, a, b, iterations, independent_variable1){
return function calculate_signed_area(independent_variable2){
let area = 0
let domain = b - a
let step = domain / iterations
for (let i = 0; i < iterations; i++){
let ri = a + (i + 0.5) * step; // midpoint of the i-th slice
let Δr = step;
let correction_factor = 1 + Δr / (2 * ri); // compute correction factor
let arg0 = independent_variable1 === 0 ? ri : independent_variable2
let arg1 = independent_variable1 === 1 ? ri : independent_variable2
area += func(arg0, arg1) * Δr * ri * correction_factor; // apply correction factor
}
return area
}
}
//_________________________________________________________
function generate_points(func, domain, iterations){
let outputs = []
for (let input = domain[0]; input < domain[1]; input+=(domain[1]-domain[0])/iterations){
outputs.push([input,func(input)])
}
return outputs
}
function stringify_points(points){
return points.map(point => `(${point[0]},${point[1]})`).join(",")
}
function calculate_area_circle_sector(r1, r2, angle){
return 0.5*(r2**2-r1**2)*angle
}
A1 = get_signed_area_function(convert_to_polar((x, y)=>-4*x/(x**2+y**2+1)), 0, 0.25*Math.PI, 1000, 1)
A2 = get_signed_area_function_polar1(A1, 0.0000000000000000001, 2, 1000, 0)
points1 = generate_points(A2, [-10, 10], 2)
A3 = get_signed_area_function_polar2(A1, 0.0000000000000000001, 2, 1000, 0)
points2 = generate_points(A3, [-10, 10], 2)
console.log(points1)
console.log(points2)
r1 = 0.00000000000000000000000000000001
r2 = 2
θ1 = 0.00000000000000000000000000000001
θ2 = 0.25*Math.PI
iterations = 1000
step_Δr = (r1 + r2) / iterations // for some algorithms that use it externally
step_Δθ = (θ1 + θ2) / iterations
answer = -2*Math.sqrt(2)*(-Math.atan(2)+2)
func = convert_to_polar((x, y)=>-4*x/(x**2+y**2+1))
// Derived: Better for manual evaluation
// int_rect(int_rect(z*r, dθ), dr)
A4 = get_signed_area_function(get_signed_area_function((r, θ) => func(r, θ) * r, θ1, θ2, iterations, 1), r1, r2, iterations, 0)
points4 = generate_points(A4, [-10, 10], 1)
// int_rect(int_rect(z, dθ)*r, dr)
A5 = get_signed_area_function(r => get_signed_area_function(func, θ1, θ2, iterations, 1)(r) * r, r1, r2, iterations, 0)
points5 = generate_points(A5, [-10, 10], 1)
// int_rect(int_rect(z*r, dr), dθ)
A6 = get_signed_area_function(get_signed_area_function((r, θ) => func(r, θ) * r, r1, r2, iterations, 0), θ1, θ2, iterations, 0)
points6 = generate_points(A6, [-10, 10], 1)
// Original: Better for algorithms
// int_rect(int_rect(z*(r+0.5*Δr), dr), dθ)
A7 = get_signed_area_function(get_signed_area_function((r, θ) => func(r, θ) * (r + 0.5 * step_Δr), r1, r2, iterations, 0), θ1, θ2, iterations, 0)
points7 = generate_points(A7, [-10, 10], 1)
A1 = get_signed_area_function_polar1(get_signed_area_function(func, θ1, θ2, iterations, 1), r1, r2, iterations, 0)
points1 = generate_points(A1, [-10, 10], 1)
A2 = get_signed_area_function_polar2(get_signed_area_function(func, θ1, θ2, iterations, 1), r1, r2, iterations, 0)
91
points2 = generate_points(A2, [-10, 10], 1)
A3 = get_signed_area_function_polar3(get_signed_area_function(func, θ1, θ2, iterations, 1), r1, r2, iterations, 0)
points3 = generate_points(A3, [-10, 10], 1)
console.log(stringify_points(points1))
console.log("error A1: ", answer - points1[0][1])
console.log(stringify_points(points2))
console.log("error A2: ", answer - points2[0][1])
console.log(stringify_points(points3))
console.log("error A3: ", answer - points3[0][1])
console.log(stringify_points(points4))
console.log("error A4: ", answer - points4[0][1])
console.log(stringify_points(points5))
console.log("error A5: ", answer - points5[0][1])
console.log(stringify_points(points6))
console.log("error A6: ", answer - points6[0][1])
console.log(stringify_points(points7))
console.log("error A7: ", answer - points7[0][1])
/*
I am trying to make two functions in javascript that calculate a double integral in polar coordinates. The first function (get_signed_area_function)
has integration with respect to θ, and the second function (get_signed_area_function_polar) with respect r. Both have inputs: func, a, b, iterations,
independent_variable1. The output of the first function can go as an input to the second function. They're very similar, just one line difference that
performs the summing operation, I suppose. The first function mimics the middle integral, and the second the outer integral. The first function has a
Riemann sum with area f(r, θ)*Δθ for one rectangle (B), which means the area under f over a circular arc within a pie piece. In other words, the area
on the round surface of a cylinder, bounded by the xy-plane, the pie piece and f. The corresponding surface is D. There is no correction to the
Riemann sum needed yet. An area function is returned with input r. So the function can return many areas depending on r. The second function is for
the outer integral, which is an extension to the Riemann sum, that corrects for the area of round strips (A) (formed by two circles, with distance Δr
from each other) instead of rectangles. I get correct answers for get_signed_area_function, so I want to extend it. Let K be the volume acquired by
nesting the first function in the first like so: get_signed_area_function(get_signed_area_function(f, ...), ...), which are two regular Riemann sums.
Let L be the volume acquired by nesting the first function in the second like so:
get_signed_area_function_polar(get_signed_area_function(f, ...), ...).
I want to find a correction factor for the summation method in function 2, which requires me to know what shape am I supposed to sum with.
My idea is to take the ratio of the areas in the xy-plane between the iterated shape of both summing methods (A/B, circular strips over rectangles),
and multiply it by volume K to get L. Surface D can be replaced by an easier surface of a curved rectangle, with area equal to that of D; that way
extrusion makes sense in multiple dimensions.
K corresponds to an an extrusion Δr distance in one dimension, and L corresponds to the extrusion along the shape of the difference of two overlaid
pie pieces (or known as the circular strips) with Δr thickness , which is not the same as extruding a surface in one dimension.
Before we go to code (I have some), is the idea correct?
*/
/* dr = 1
dA = 1 * 10 = 10
A = 4*10 = 40
r=[1,2,3,4,5]
vorm: trap (geen lijn)
Geval1:
diepte=10
z=[1,2,3,4,5]
I_cartesisch = 1*10+2*10+3*10+4*10 = 100
I_cartesisch_rand1 = 1*10 = 10
I_cartesisch_rand2 = 4*10 = 40
I_pool_rand1 = (1*(r2^2-r1^2)*θΔ)/2 = 0.5*(4-1)θΔ = 1.5θΔ = 5
I_pool_rand2 = (4*(r2^2-r1^2)*θΔ)/2 = 4*0.5*(25-16)θΔ = 18θΔ = 60
A = ((r2^2-r1^2)*θΔ)/2 = ((25-1)*θΔ)/2 = 12θΔ --> 40 = 12θΔ --> θΔ = 10/3
bijkomend_I_pool_randen = I_pool_rand1 + I_pool_rand2 = 65
bijkomend_I_cartesisch_randen = I_cartesisch_rand1 + I_cartesisch_rand2 = 50
Cirkel sector inhouden optellen aan beide kanten levert ongelijke waardes, in tegenstelling tot het optellen van rechthoek inhouden aan beide
kanten:
let step = 0.1; for (let i=1; i<5; i+=step){ console.log(i*calculate_area_circle_sector(i,i+step,10/3) + (5-i+1-step)*calculate_area_circle_sector(5-
i+1-step,5-i+1,10/3)) }
Geval2:
z=[5,5,5,5,5] */
92
3.3
Grid Transformer (Javascript)
I made a grid transformer in Javascript in order to visualize math transformations. The user inputs a
transformation function, some display settings, and grid line markings into the plot_grid function.
The green dot represents the input coordinate, and the red dot the output coordinate. The grid line
markings are in green and red too; one line is given by one coordinate-component. There are sliders
to change the input coordinate. The transformation is from Cartesian to some user provided
function.
The following example uses the call:
plot_grid(30, 3, 200, 0.1, 300, 300, {x: (u,v)=>(Math.sqrt(u)*v**(1/4))**(4/3), y:
(u,v)=>(Math.sqrt(v)*u**(1/4))**(4/3)}, "black", 0.005, {u:[0.5,1], v:[1/3,1]})
More transformations:
93
3.4
Parametric
Parametric
Curves
on
Surfaces
Surfaces
(
CalcPlot3D)
In CalcPlot3D, Given an equation for a surface
(1)
, I found a way to project a parametric curve on it
by
choosing x=t,
setting y(t) to some expression so that the curve represents the flat shape I wish to
project
with coordinates (t, y(t))
, and then setting its third dimension equal to (1) solved for z with
the other variables substituted for x=t and
y=
y(t) of the 2d curve, to get z(t), and thus the projected
curve (3d)
described by (t, y(t), z(t))
.
The surfaces here are a parabolic cone
and a saddle
:
94
4
Programming Communities
4.1
Bug
reports
reports
for
Inkscape
Inkscape
Here are some bugs and feature requests I reported for Inkscape on Gitlab:
https://gitlab.com/inkscape/inbox/issues/?
scope=all&utf8=%E2%9C%93&state=opened&author_username=darthglowball
Preview:
•
Feature request: add an "Attribute editor" or "key-value editor" header
•
Feature request: container elements (<svg>, <g>) fitting the size and position of their
descendants' bounding box when grouped & container descendants' transform staying the
same when ungrouped
•
Bug: <svg> element should be treated as a <g> when ungrouping & feature request: there
should be an option to group elements with <svg>
•
transform attribute on <svg> element doesn't work
•
Feature request: being able to edit attribute value in attribute editor by pressing enter and
being able to navigate with keyboard
•
Undoing an attribute value in the attribute editor causes undoing of the last action that isn't
the editing of the attribute value
•
Feature request: better XML editor node identification by attributes, semi-unique names
("non-unique id's") and less crowdedness
•
Feature request: paste element into other element
•
svg element doesn't have any visual selection boundary when selected
•
svg element can't be moved by mouse dragging or keyboard arrows (or transformed by those
means)
4.2
Bug
reports
reports
for
Pywebview
Pywebview
On Github you can find my bug reports:
•
Creating a CEF window and destroying it repeatedly causes memory leak
•
Modules are fetched only sometimes by the HTTP server, and clicking on Developer Tools
fetches/runs everything
•
CEF Javascript module import error: Failed to load module script: The server responded
with a non-JavaScript MIME type of "text/plain"...
•
A call from CEF to js_api to a Python standard json library's dumps() returns a promise, but
the resolved promise returns an object and not a string
95
4.3
Bug
reports
reports
for
Brython
Brython
On Github you can find my bug reports:
•
A Brython keyboard event handler function is 73x slower in Chrome and 143x slower in
CEF than its Javascript equivalent
•
Importing re or copy modules give 100% CPU indefinitely
•
KeyboardEvent.repeat is always false in CEF
•
Import can't find Regex module
•
Console message spam of html element code when setting attribute of element from a
function in window
•
Setting Javascript JSON parse()'s result to a Python class attribute, and using that class
attribute in stringify() causes "converting circular structure to JSON" error
•
element.removeEventListener doesn't remove a mousemove/pointermove event
•
CDATA in multiple script elements gives syntax error
4.4
Bug
reports
reports
for
Transcrypt
Transcrypt
On Github you can find my bug reports:
•
del has Javascript behaviour when used on a list item
•
Non-literal list concatenation and sorting: TypeError: t.py_sort is not a function
4.5
Bug
reports
reports
for
svgwrite
svgwrite
On Github you can find my bug reports:
•
<text> initialization method's x, y arguments don't accept decimals
4.6
Bug
reports
reports
for
cefpython
cefpython
On Github you can find my bug reports:
•
CEF Javascript module import error: Failed to load module script: The server responded
with a non-JavaScript MIME type of "text/plain"...
•
In an html file with the structure: body > svg > g > foreignObject > div > div, the innermost
element has an incorrect position
4.7
Bug
reports
reports
for
mrab-regex
mrab-regex
On Bitbucket you can find my bug reports / feature requests of Matthew Barnett's Python Regex
module:
•
Performance enhancement by having a "return/exit search" token
96
5
Mechanical Builds
5.1
Flywheel
I
built
a flywheel
with
a generator on a re-used base
, on which I can mount
physics experiments.
The metal base was made by
a project
group I was in at MBO
for an unrelated project
, and the rest
made
by me.
97
6
Designs (model
s
, effects,
scenes,
games)
Here I show some
2D and
3D models, GUI designs, games and game demo's I made.
6.1
Uncategorized
98
99
100
101
102
6.2
“Free
energy”
energy”
rising
and
falling
falling
weight
carried
carried
by
floater
floater
My idea. I once determined the reason why it cannot work.
It was drawn with OpenOffice Draw.
6.2.1
Schematic
103
6.2.2
The variables and their
metadata
104
Variable
Name
Abbreviation
Input
Output
Relevancy cycle
floater width
w
f
x
floater height
h
f
x
floater protrusion height
h
fp
x
floater submersion height
h
fs
x
fluid main tube width
w
fmt
x
fluid main tube height
h
fmt
x
fluid in main tube pouring height
h
fimtp
x
fluid in main tube minimum height
h
fimtm
x
fluid container width
w
fc
x
fluid container height
h
fc
x
fluid return tube width
w
frt
x
fluid return tube pouring height
h
frtp
x
lifting object mass
m
lo
x
floater mass
m
f
x
fluid in container mass
m
fic
x
displaced fluid by floater mass
m
dfbf
x
floater – lifting object connection
beam mass
m
flocb
x
movable objects mass
m
mo
x
fluid container base angle
θ
fcb
x
gravitational acceleration
6.3
Drawings
6.3.1
No reference / imagined
I am guessing I had drawn these at 16 years old.
105
Red Alert & Redneck Rampage:
Game enemy & allied objects,
partially redesigned
. I am guessing I
was 16 years old.
106
6.3.2
Reference picture hand traces
I traced these with a pencil ontop of a reference
picture
.
107
6.3.3
Reference object
distant
sketches
I sketched these with a reference object in the distance.
108
6.3.4
Collages
109
;