moved blog articles
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: 'do the work yourself not the server : Using githooks over actions for the creation of artifacts'
|
||||
description: 'why make a server do work when you can do it yourself'
|
||||
heroImage: '../../assets/article_hero_images/hook.gif'
|
||||
pubDate: 'Feb 15 2026'
|
||||
---
|
||||
|
||||
I know that's a title and half, and now you are wondering "*why would I do something myself rather than a server*".
|
||||
But before I get answering that question, i will start by breaking down and defining each part within that title to get a clearer image on exactly what I'm talking about (since there are quite a few buzz words in there).
|
||||
To start what is an artifact, artifact can refer to wide variety of outputs such as header files, documention, test suites, the source code itself, the standalone executable, etc.
|
||||
So you can see by the definition artifacts although vague are a key and necessary part of development.
|
||||
So why auto create them, the answer is simple because you **will** forget.
|
||||
|
||||
Now that the second part of the title is defined lets define the first part, githooks vs actions.
|
||||
Actions are a key idea within Continuous Integration and Continuous Delivery (CI/CD) where on every small change to your main branch (CI) redeploys the code (CD), this could be running tests on your code, compiling your library, redeploying your website, generating new documentation, formatting your code etc.
|
||||
However these tend to run on automation servers such as jenkins, github actions, gitea actions etc.
|
||||
Where githooks are bash scripts that run either before or after some command you run with git, i.e. `git push`, `git commit`, `git add` and therefor run locally on your machine. Addtionally, Since githooks are just actions they can do all the things actions can do.
|
||||
|
||||
So as you can see both githooks and actions and are both very powerful.
|
||||
However why pick hooks over actions, one main factor *simplicity* actions rely on a server to preform them and for the steps within those actions to be efficient.
|
||||
Addtionally, to run an action you have to setup and enviroment for running the script which takes long to setup and passing generated artifacts between steps can become a headache especailly when you run multiple at one time.
|
||||
Although, I'm not saying you should never use actions, just the vast majority of things you do with actions can be done with a git hook before you push your code.
|
||||
The time and place where you should be setting up actions is when you *need* one source of truth for your design.
|
||||
Since actions reduce the "it works on my machine" issues by forcing you to set up a system where the code can run each time it creates a point where your code can be testing on a system that is not your own.
|
||||
|
||||
What im trying to get at is actions should be used for things like static site generators, compiling the website in to an HTML file on your machine then hosting it somewhere else or building and auto committing documention for your projects rather than having an action do it.
|
||||
A great example for this and the reason I'm writing this was the comparison between my [kicad project template that used actions](https://git.sirlilpanda.studio/sirlilpanda/kicad-project-template) and my [kicad project template that doesnt](https://git.sirlilpanda.studio/sirlilpanda/kicad-project-template-actionless).
|
||||
These are templates projects for kicad a pcb design tool, with the primary reason for these templates needing to incorporate some form of tool for added the generated schematics, PCB gerbers, images of the PCB itself and the running of the electronic and design rules checkers.
|
||||
However the template project that uses actions can take up to 2 mins to run on GitHub servers ( I still have yet to properly set it up on Gitea), but the action one runs in about 30 seconds before it pushes up to Gitea or another forge service.
|
||||
so to summarise, actions are not useless quite the opposite however there are a vast number of use cases where a simple githook before pushing is all that is needed.
|
||||
|
||||
|
||||
githooks can also be run on a server like actions but thats too much to cover here.
|
||||
|
||||
# extra resources you man want
|
||||
|
||||
- CICD : https://en.wikipedia.org/wiki/CI/CD
|
||||
- githooks : https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
|
||||
- actions : https://docs.github.com/en/actions
|
||||
- why actions may not be that good : https://ziglang.org/news/migrating-from-github-to-codeberg/
|
||||
62
src/content/blog/a-bullitin-board-for-a-modern-age.md
Normal file
62
src/content/blog/a-bullitin-board-for-a-modern-age.md
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: 'A bulletin board for a modern age'
|
||||
description: |
|
||||
bulletin boards have existed since the time of the ancient greeks however use has dwindled
|
||||
since the advent of the internet, therefor in this article i propose a new idea to bring this
|
||||
conecpt in to the modern world.
|
||||
heroImage: '../../assets/article_hero_images/bulletin_board.png'
|
||||
pubDate: 'Mar 8 2026'
|
||||
---
|
||||
|
||||
|
||||
Since the time of the ancient Greeks people have been using bulletin boards to share information within local communities without having to tell each person individually.
|
||||
Although bulletin boards also have a secondary aspect that most people overlook they created spaces around them to allow people to engaged in local discussions about the notices on the board.
|
||||
These discussions about the bulletin board essentially convert this space from just a place to get information to kind of third space where people can gather and discuss what is happening.
|
||||
However, ever in more "recent" times (1980) people moved to posting this information on "local" bulletin board systems (BBS).
|
||||
These systems had the same concept as the normal bulletin boards although instead of printing out
|
||||
the poster or message and sticking to the local bulletin board this system would allow you to call in and leave a message on what is essentially a glorified answering machine.
|
||||
This "newer" still kept the local nature of the bulletin board system as you would have to know the
|
||||
number to call in to as well as cross-country calls being expensive and carriers not talking to each other.
|
||||
Not to discount that on these BBSes since they were local to the places uses them this too created a form of virtual third space where people could discuss local happening continuing this trend.
|
||||
This however was changed when high speed links started to forum between these smaller clusters of BBS systems and really in turn starting to create the world wide web as we know it.
|
||||
Alas, as the internet has taken off within our modern ages BBSes have almost ceased to exists with a few still floating around due to them however most requiring a telnet program to access.
|
||||
This decline of BBSes is in addition to them now becoming connected to the world wide web losing that local communities' aspect.
|
||||
So that was the past, in the present most people have turned to using Facebook groups or discord servers to create what is essentially a bulletin board for there local communities, this is an excellent use of the tools they have on hand and modernize the system however these are not without their faults.
|
||||
However, I argue that these are not true bulletin boards and instead just another form of virtual third space in addition they lose some of the soul of what made a bulletin board it became less of a place to post information and more of a place to request information.
|
||||
This can be in the form of asking about local road works this would never be posted on a bulletin board because you would instead just ask someone instead.
|
||||
This is not to discount the fact that people still use this for its indented purpose, is more this have further evolved in to something a bulletin board is not.
|
||||
|
||||
Therefor I propose an idea a modern take on a bulletin board that combine the use of new technology such as phones and IOT devices. This idea will have the following requirements written in the EARs format with some additional rational for why:
|
||||
- the device shall allow for the posting and viewing of bulletins
|
||||
|
||||
cant have a bulletin board without bulletins
|
||||
|
||||
- the device shall run its own wireless access point
|
||||
|
||||
since people dont want to print out a poster and stick it to a bulletin board and every one normally has some device they can just connect to a local network on may as well use that device instead to show everything
|
||||
|
||||
- the device shall host the bulletins on some web server
|
||||
|
||||
This is just the easiest method of displaying content in a reasonable manner to the user additionally this means the bulletin board can be customised.
|
||||
|
||||
- the device should not be connected to the internet for remote posting or veiwing.
|
||||
|
||||
This is done to primary keep the idea of letting people meet naturally in this now hybrid virtual and physical third space
|
||||
|
||||
The reason why there are so few requirements is not to limit what one of these bulletin boards can be.
|
||||
It could be as simple as an ESP32 running a web server that people can connect to and upload their bulletins or as complex as a computer and router with a QR code on a monitor that also show some of the bullets so you dont need to open your phone.
|
||||
Now this would normally be the part where i show a proof of concept device however i have yet to make one this is purely a theoretical idea at this point.
|
||||
However, when i do implement this i will be sure to provide an update on how it went.
|
||||
|
||||
|
||||
|
||||
### Some additional reading
|
||||
- https://www.thethirdspacecollective.com/about-3
|
||||
- https://en.wikipedia.org/wiki/Bulletin_board
|
||||
- https://www.youtube.com/watch?v=I18ifd8I6P8
|
||||
- https://www.youtube.com/watch?v=ItXCjhlGd7c
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
26
src/content/blog/adding-a-guest-list-to-the-website.md
Normal file
26
src/content/blog/adding-a-guest-list-to-the-website.md
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: 'Adding an guest book to my website'
|
||||
description: |
|
||||
to attempt to give some more life to my website and instead of letting it sit as a static statue one the internet, i have decided to implement a guessbook so i can see the people who are checking out my website and so i can check out theirs if they have one.
|
||||
heroImage: '../../assets/article_hero_images/guest_book.png'
|
||||
pubDate: 'Mar 15 2026'
|
||||
---
|
||||
|
||||
Guest books on websites have been around since the 90s. They were originally created so to allow for general vistor comments on your website, since at that point social media hadn't grown to the point it is now. Therefor I have decided to create a guestbook on my website as part of it rework.Currently, the only way people could leave feedback would be to open an issue on git or email me. These methods of feedback both feel inadequate and not the right tools for the job. Emails would allow for me to see vistor comments but other people could not see them less I add them to the website and that doesnt feel like a vaild feedback its more like cherry-picked corporate reviews. issues on git is another option for it however those are out of the way and not easily accessible as well as those issues should be for things that are wrong with the website.
|
||||
|
||||
Now on to the implementation of this new feature. Unlike the other sites that have a guestbook like on neocities, that use something like atabook in the backend, I have decided to go the hard way by creating my own. Luckly for me I already use Cloudflare to host my website (even though I do have my own infrastructure I could run it on but thats a story for another day). Because of this i have access to Cloudflare's D1 storage objects so I dont have to deal with hosted a database and keeping that secure, even though there will be nothing of note on it. These storage objects are an SQL database that exists in Cloudflare's servers, however there is one major difference between this database and other databases. Cloudflare keeps these databases are serverless and not have an external endpoint to access them. Instead, these D1 databases have to be binded to the process locally through environment variables. This not only keeps them secure but also stop any api keys from being exposed within a repo. However, there are downsides to using this type of storage mainly the hidden costs of running it, although it would take a very long time till i reach the cap of when i would have to start paying, per day Cloudflare provides 5 million free reads and 100,000 free writes, and 5GB of free data. This paired with some light rate limited should allow the guestbook to run completely fine without being charged
|
||||
|
||||
So how did i implement this, to start i first tried to use astros own DB abstraction layer since it used drizzle ORM under the hood and cloudflare d1 storage objects support drizzle ORM as a first class interface. However, astro abstracts out drizzle client too far for me (a non web dev) to be able to use it with Cloudflare, there might be a way too there might not be. therefor i instead followed a tutorial by kevin kipp on adding it to my astro app, upon completing that i changed the schema to be an ID, a name, a optional link to their own website, a message they want to write and lastly the date written. This was the minimum amount of information needed for a guest book log. Yes in theory you could remove the website and have people include that in their message but that felt too disconnected and people would then have to shoe horn it in to their comment and that didnt feel natural for the user. i might also try add an option for the 88x31 website buttons as well, but that will be in the future.
|
||||
|
||||
You may have noticed that the guest book isnt here. Well thats because i have started yet again another new repo for this website, mainly due to the fact that astro has changed within recent versions and just starting again and removing the current tech debt i have is the easier option.
|
||||
|
||||
but if you want to sign it before anyone else see it on this website then you can check it out here https://website-rework.sirlilpanda.workers.dev/ yes its very unfinished im attempting to work on a layout for it since top bar is just not the play any more.
|
||||
|
||||
# Resources
|
||||
how to actually implement a cloudflare D1 storage with astro : https://kevinkipp.com/blog/going-full-stack-on-astro-with-cloudflare-d1-and-drizzle/
|
||||
astros own DB : https://docs.astro.build/en/guides/astro-db/
|
||||
free guestbook service : https://atabook.org/
|
||||
## other sites with guest books
|
||||
https://c-nder.neocities.org/guestbook
|
||||
https://advelos.moe/
|
||||
https://melonking.net/melon?z=/hidden/loading
|
||||
492
src/content/blog/c-fun.md
Normal file
492
src/content/blog/c-fun.md
Normal file
@@ -0,0 +1,492 @@
|
||||
---
|
||||
title: 'the fun niche parts of c'
|
||||
description: 'the article will go over some of the more less common and useful "features" in c'
|
||||
heroImage: '../../assets/article_hero_images/c.png'
|
||||
pubDate: 'Mar 1 2026'
|
||||
---
|
||||
|
||||
|
||||
|
||||
This article will be slight less fouced on words and more fouced on the cool niche things c can do or
|
||||
you can do in c. Most everything in here can be done with most modern c compilers and will include
|
||||
a combination of both macro shenanigans and "hidden" parts of the c lang. this will not cover the fun
|
||||
[trigraphs](https://en.wikipedia.org/wiki/Digraphs_and_trigraphs_(programming)) within c as these have
|
||||
been removed from the language since all keyboard support all the ascii chars needed for c.
|
||||
|
||||
## Bit annotations
|
||||
|
||||
Bit annotations are a feature with c that allows you to state the width of an integer type within structs.
|
||||
As can be seen in the example below all the bit_fields here will only be 4 bits across meaning the total
|
||||
size of this struct will be 16 bits (you can run sizeof to confirm this), this is excellent within
|
||||
embedded systems when memory is a very important fact within your code.
|
||||
``` c
|
||||
typedef struct {
|
||||
unsigned bit_field_1 : 4;
|
||||
unsigned bit_field_2 : 4;
|
||||
unsigned bit_field_3 : 4;
|
||||
unsigned bit_field_4 : 4;
|
||||
} temp_t;
|
||||
```
|
||||
|
||||
## the slide operator
|
||||
|
||||
The slide "operator" is a funny use of how c process its operators, when looking at the example below
|
||||
it appears that the `-->` in the while loop is a genuine operator, that reads like
|
||||
> i goes to zero
|
||||
however if you look sightly closer this is actually just a `--` decrement operator and `>` greater than
|
||||
equally in one. The reason this works is that both the decrement and increment operators will return
|
||||
the number before or after the operation has happened (depending on what side its on). Additionally, c
|
||||
does not care about the spacing of the tokens within the language and therefor you can place these
|
||||
operators right next to each other.
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
int i = 10;
|
||||
|
||||
while (i --> 0) {
|
||||
printf("%d \n", i);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
## VA macros
|
||||
|
||||
VA or variadic macros are a type of macro that allows for an unknown number of arguments, this is
|
||||
especially useful for if you want to alias the `printf` function to something else. A use case for this
|
||||
could be if you want to change the output in an embedded device from a debugger to a file system on
|
||||
board for logs when you do a release build of it.
|
||||
```c
|
||||
|
||||
#define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)
|
||||
|
||||
// these mean the "," is optional and wont be added if no __VA_ARGS __ are provided
|
||||
#define eprintf(format, ...) \
|
||||
fprintf (stderr, format __VA_OPT__(,) __VA_ARGS__)
|
||||
|
||||
// or
|
||||
|
||||
#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__)
|
||||
|
||||
```
|
||||
|
||||
|
||||
## The strange macro constants
|
||||
|
||||
There are several strange but also very useful macro constants that get defined before compilation,
|
||||
or at least defined by gcc. Below are these definitions as well as the preprocessor builtins operations
|
||||
|
||||
```c
|
||||
// macros builtins
|
||||
#define str(s) #s // this converts symbols like varible names in to strings
|
||||
#define concat(s1, s2) s1##s2 // this is used on code symbols like varible names
|
||||
#define concatStrings(s1, s2) s1 s2 // yeah strings defined as "something" "somethingelse" will just merge
|
||||
// the useful ones
|
||||
__LINE__ // int of the line this is defined on
|
||||
__FILE__ // full file path this is defined in
|
||||
__DATE__ // current date during compilation
|
||||
__TIME__ // current time during compilation
|
||||
__BASE_FILE__ // the file its in not the file path
|
||||
|
||||
|
||||
|
||||
// the strange ones
|
||||
__COUNT__ // increase by one everytime is used
|
||||
__INCLUDE_LEVEL__ // a constant int for the depth that include files are nested
|
||||
|
||||
```
|
||||
|
||||
## For each macros
|
||||
|
||||
Foreach macros are the perfect thing when you find out they exist. They are used within in some fairly
|
||||
large libs like [fff](https://github.com/meekrosoft/fff). The basic principle behind these macros are
|
||||
each of the 10 `FOREACH_x` steps take the next most variadic argument and passes it to the `FOREACH_(x-1)`
|
||||
macro. The `N_VA_ARGS` is the one that does this step of taking the last arg and passing it to the
|
||||
`FOREACH_x macro`.
|
||||
If you for some ungodly reason you need more then 10 i have created a script that will
|
||||
generate the foreach macro for any given length which you can find [here](https://github.com/sirlilpanda/c-comptime-json-fmt-string/blob/main/gen_foreach.py). This one looks different but works in the exact same way.
|
||||
```c
|
||||
|
||||
|
||||
#define N_VA_ARGS_(_9,_8,_7,_6,_5,_4,_3,_2,_1, N, ...) N
|
||||
#define N_VA_ARGS(...) N_VA_ARGS_(__VA_ARGS__ __VA_OPT__(,) 9,8,7,6,5,4,3,2,1,0)
|
||||
|
||||
#define FOREACH_0(FN, ...)
|
||||
#define FOREACH_1(FN, E, ...) FN(E)
|
||||
#define FOREACH_2(FN, E, ...) FN(E) FOREACH_1(FN, __VA_ARGS__)
|
||||
#define FOREACH_3(FN, E, ...) FN(E) FOREACH_2(FN, __VA_ARGS__)
|
||||
#define FOREACH_4(FN, E, ...) FN(E) FOREACH_3(FN, __VA_ARGS__)
|
||||
#define FOREACH_5(FN, E, ...) FN(E) FOREACH_4(FN, __VA_ARGS__)
|
||||
#define FOREACH_6(FN, E, ...) FN(E) FOREACH_5(FN, __VA_ARGS__)
|
||||
#define FOREACH_7(FN, E, ...) FN(E) FOREACH_6(FN, __VA_ARGS__)
|
||||
#define FOREACH_8(FN, E, ...) FN(E) FOREACH_7(FN, __VA_ARGS__)
|
||||
#define FOREACH_9(FN, E, ...) FN(E) FOREACH_8(FN, __VA_ARGS__)
|
||||
|
||||
#define FOREACH__(FN, NARGS, ...) FOREACH_##NARGS(FN, __VA_ARGS__)
|
||||
#define FOREACH_(FN, NARGS, ...) FOREACH__(FN, NARGS, __VA_ARGS__)
|
||||
#define FOREACH(FN, ...) FOREACH_(FN, N_VA_ARGS(__VA_ARGS__), __VA_ARGS__)
|
||||
|
||||
```
|
||||
|
||||
|
||||
## defer macros
|
||||
|
||||
defer macros yet another thing that is wonderful once you find out its exists, or if you havent heard of
|
||||
defer you are in for a treat. defer works by allowing some bit of code to run both before and after a block
|
||||
so say you are opening a file or socket and you need to close it by the end of scope. Instead of writing
|
||||
the cleanup code at the end you write both the start and end code within the start and end func of the
|
||||
defer statement and it does the work for you. This macro works by using the fact `for` statements have both
|
||||
a start statement i.e. `int i = 0` (the bit of code that gets run before anything in the block is run) and
|
||||
an end statement i.e. `i++` the statement that runs once the middle has been checked.
|
||||
|
||||
and if you are wondering how `int macro_var(_i_) = (start_func, 0)` assigns `macro_var(_i_)` to zero,
|
||||
so am i, i will update this when i work this out.
|
||||
|
||||
```c
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define macro_var(name) name##__LINE__
|
||||
#define defer(start_func, end) for ( \
|
||||
int macro_var(_i_) = (start_func, 0); \
|
||||
!macro_var(_i_); \
|
||||
(macro_var(_i_) += 1), end)
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
defer(puts("hello world"), puts("goodbye world")){
|
||||
puts("im in a deferblock ma");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## including links in your code
|
||||
|
||||
This isnt even a feature nor should it ever be used its just cool, you can just add links in to your code
|
||||
since the `https:` part is just a goto label and the `//` make everything after on that line a comment.
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc,char* argv[]) {
|
||||
|
||||
https://sirlilpanda.studio
|
||||
|
||||
printf("hello world");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## kwargs in c
|
||||
|
||||
With a little macro magic you can have keyword arguments in c. This is all thanks to the fact that
|
||||
during struct creation fields can be redefined multiple time with the last on being used. I believe
|
||||
this is an artifact left during the K&R style era where struct fields had to be defined one at a time
|
||||
after the variable definition.
|
||||
|
||||
So the way this code works, is there is an `internal_print_string` function, that takes in the normal
|
||||
arguments and the struct that defines all the keyword arguments in it. This function is then wrapped in a
|
||||
macro, where the default argument of the kwargs are defined where the VA args are defined at the end of
|
||||
the struct so the previously defined fields are overwritten with the new values.
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
int left_padding;
|
||||
int right_padding;
|
||||
char* author_name;
|
||||
} FuncKwargs_T;
|
||||
|
||||
#define print_string(str, ...) \
|
||||
internal_print_string( \
|
||||
str, \
|
||||
(FuncKwargs_T){ \
|
||||
.left_padding = 5, \
|
||||
.right_padding = 5, \
|
||||
.author_name = "unknown",\
|
||||
__VA_ARGS__ \
|
||||
} \
|
||||
) \
|
||||
|
||||
static void internal_print_string(char* string, FuncKwargs_T kwargs) {
|
||||
|
||||
printf("%s :", kwargs.author_name);
|
||||
for (int i; i < kwargs.left_padding; i++) putchar(' ');
|
||||
printf("\"%s\"", string);
|
||||
for (int i; i < kwargs.right_padding; i++) putchar(' ');
|
||||
return;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
print_string("hello_world", .author_name = "sirlilpanda");
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Documenting code examples
|
||||
|
||||
When documenting code example many people place the examples inside comments above the function.
|
||||
however this has one major problem no *syntax highlighting* so instead you can define the example
|
||||
within a pre-processor directive that will never run i.e. `#if 0`. As if `#if 0` evaluates and
|
||||
the example code is added i think you have much larger problems. Finally, if worse comes to worst
|
||||
and the thing that handles the highlighting where it be an lsp or treesitter or just a lua filter
|
||||
does highlight it you wernt going to get highlighting anyway if it was in a comment.
|
||||
|
||||
```c
|
||||
static void internal_print_string(char* string, FuncKwargs_T kwargs) {
|
||||
#if 0
|
||||
print_string("hello_world", .author_name = "sirlilpanda");
|
||||
#endif
|
||||
|
||||
printf("%s :", kwargs.author_name);
|
||||
for (int i; i < kwargs.left_padding; i++) putchar(' ');
|
||||
printf("\"%s\"", string);
|
||||
for (int i; i < kwargs.right_padding; i++) putchar(' ');
|
||||
return;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Include just include a file
|
||||
|
||||
When the preprocessor runs and hits your include directive it just reads that file and slaps the
|
||||
content of that file in your source code. Therefor you can include large data within your source
|
||||
code while its in another file. This can be very used for things like shaders and bitmap fonts.
|
||||
|
||||
```c
|
||||
// ============== my_text_file.txt ==============
|
||||
// "hello world"
|
||||
// ============== my_text_file.txt ==============
|
||||
|
||||
const char string[] = #include "my_text_file.txt";
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
## Defining includes before including them
|
||||
|
||||
defined run before the includes within the pre-processor and you can therefor define your includes
|
||||
file header path as a define. This could possibly come in useful if you have different platforms and
|
||||
only want one include for some reason.
|
||||
|
||||
```c
|
||||
#define PRINTING_H <stdio.h>
|
||||
#include PRINTING_H
|
||||
|
||||
int main() {
|
||||
printf("hello world\n");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
## looping over arrays with falsely terminating values
|
||||
|
||||
Instead of using a while loop to keep checking if that char isnt null just use a for loop.
|
||||
NOTE THIS CODE does mutate `somedata`.
|
||||
|
||||
```c
|
||||
|
||||
char* somedata = "hello world\n";
|
||||
|
||||
for (; *somedata; somedata++) {
|
||||
putchar(*somedata);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## all string types
|
||||
|
||||
c surprisingly has multiple string types these are the normal ascii strings, L string and u strings
|
||||
(c++ also has R strings but we only deal in c like sane people).
|
||||
u strings just tells the compiler that these chars in the string are unsigned.
|
||||
U strings not to be confused with u strings tell the compiler that the characters are unsigned ints.
|
||||
L strings on the other hand just tell the compiler the characters are just ints.
|
||||
|
||||
```c
|
||||
char string[] = "hello world\n";
|
||||
char string[] = u8"hello world\n";
|
||||
|
||||
unsigned char string[] = u"hello world\n";
|
||||
unsigned char string[] = U"hello world\n";
|
||||
|
||||
int string[] = L"hello world\n";
|
||||
```
|
||||
|
||||
## defines can have nothing in them
|
||||
|
||||
Just as the title said, all defines are, are just a find and replace and you can replace it with
|
||||
nothing. Now go enjoy putting `fucking` all over your code.
|
||||
|
||||
```c
|
||||
#define fucking
|
||||
|
||||
// completely vaild
|
||||
int fucking main(){
|
||||
return 0
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## unions for differnt names for struct fields
|
||||
|
||||
Instead of using unions for a real purpose, you can instead use them for changing the name of how you
|
||||
index in to different struct fields. This can actually be quite useful when writing something like a
|
||||
vector struct, sometimes the math looks better when its in terms of x, y, z, or maybe you are doing
|
||||
quaternions and just need something for the imaginary part, or maybe even R, B, G for when you are
|
||||
dealing with colours.
|
||||
|
||||
```c
|
||||
typedef union {
|
||||
float vec[3];
|
||||
|
||||
// these need to stay unnamed
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
struct {
|
||||
float i;
|
||||
float j;
|
||||
float k;
|
||||
};
|
||||
struct {
|
||||
float a;
|
||||
float b;
|
||||
float c;
|
||||
};
|
||||
|
||||
}Vec3_t;
|
||||
```
|
||||
|
||||
## Implicit delectation
|
||||
|
||||
This was a strange bit of the c compiler when it did not treat implicit delectation of function as
|
||||
warning, when c99 dropped this was fixed. I believe this was extensively used in some early games, i
|
||||
will attempt to find a reference for this and update this page. However, even though i cant show it
|
||||
here i have seen this done in early 2000 production code.
|
||||
|
||||
```c
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("num : %d\n", add(2, 4));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int add(int n1, int n2) {
|
||||
return n1 + n2;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## force complier hints
|
||||
|
||||
Think your smarter than the compliers branch predictor then you can tell it what way it should
|
||||
go instead with these builtin functions. These functions tell the compiler that x is going to be
|
||||
either 1 or 0 and that's how you should predict it going. The reason for the double exclamation marks
|
||||
is to ensure that x gets converted in to a boolean before being passed in to the function.
|
||||
|
||||
|
||||
```c
|
||||
#define likely(x) __builtin_expect (!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect (!!(x), 0)
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Yoda conditions
|
||||
|
||||
Yoda conditions are a method of catching possible typo in your code before it complies. This works by
|
||||
changing how you write if statements from `thing to check` `some operation` `other thing` to `other thing`
|
||||
`some operation` `thing to check`, this normally catches bugs like if only one equals sign was added
|
||||
instead of 2, as normally the thing you are checking against is a constant value and therefor cannot be
|
||||
assigned to and errors out.
|
||||
|
||||
```c
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
int val = 32;
|
||||
|
||||
// will alway make val 32
|
||||
if (val = 32) puts("is 32");
|
||||
// will error
|
||||
if (32 = val) puts("is 32");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
## vlas can be use to tell the complier that something exists/is not null
|
||||
|
||||
i will add additional explantation to this soon once i do a bit more research on it but just no you
|
||||
can do this because of VLAs.
|
||||
|
||||
```c
|
||||
// single objects
|
||||
void func(T *obj); // obj can be null
|
||||
void func(T obj[static 1]); // obj must exist and cant be null
|
||||
|
||||
// multiple objects
|
||||
void func(T arr[N]); // some obj can be invalid or null
|
||||
void func(T obj[static N]); // objs must exist and cant be null
|
||||
|
||||
```
|
||||
|
||||
## k&r styles c this is sadly no longer vaild
|
||||
k&r styles c was an old style of writing c when the language was still quite malleable, sadly modern
|
||||
compilers have dropped support for processing this, but you can still use something like setting the
|
||||
standard to `-std=c99` and it should compile. Admittedly when you look at it, you can see why our modern
|
||||
style has taken off instead.
|
||||
|
||||
```c
|
||||
// K&R syntax
|
||||
int foo(a, p)
|
||||
int a;
|
||||
char *p;
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
## Auto free in c
|
||||
|
||||
This is a feature of gcc and some other compilers that allows for a cleanup attribute to be added to
|
||||
variable that bind some function to run after it has left scope. This is really useful as a defer statement
|
||||
instead of the dodgy defer macro that i showed previously.
|
||||
|
||||
```c
|
||||
void cleanup_free(void *p_) {
|
||||
void **p = (void**)p_;
|
||||
free(*p);
|
||||
}
|
||||
|
||||
void f(const char *str) {
|
||||
__attribute__((cleanup(cleanup_free))) char * str_copy = strdup(str);
|
||||
// Do something with str_copy
|
||||
// ...
|
||||
// cleanup_free(&str_copy) is called here
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## extra reading and where i got these things from
|
||||
- Programming in Modern C with a Sneak Peek into C23 : https://www.youtube.com/watch?v=lLv1s7rKeCM
|
||||
- A look back at original style C : https://www.youtube.com/watch?v=uOKzkr7uv9E
|
||||
- Other Built-in Functions Provided by GCC : https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
|
||||
- Specifying Attributes of Variables : https://www.cse.unr.edu/~sushil/class/cs202/help/man/gcc-2.7.0/gcc_82.html
|
||||
- Kwargs in c : https://gist.github.com/RickBarretto/ed0065c1a2144deb4b3250ce125956b0
|
||||
- Common Predefined Macros : https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
|
||||
- Foreach macro in C : https://sgf4.github.io/posts/foreach-macro/
|
||||
- Yoda_conditions : https://en.wikipedia.org/wiki/Yoda_conditions
|
||||
- force branch prediction : https://stackoverflow.com/questions/30130930/is-there-a-compiler-hint-for-gcc-to-force-branch-prediction-to-always-go-a-certa
|
||||
|
||||
109
src/content/blog/c-result-type.md
Normal file
109
src/content/blog/c-result-type.md
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
title: 'c result type'
|
||||
description: 'a fun little pre processor macro for adding rust type errors in to c'
|
||||
pubDate: 'Feb 29 2024'
|
||||
heroImage: '../../assets/article_hero_images/c_result_type.png'
|
||||
---
|
||||
|
||||
the c result type was made as i wanted better error handling in c than always having to write:
|
||||
```c
|
||||
if (func_that_could_error(__VA_ARGS) == -1){
|
||||
perror(ERROR_MESSAGE);
|
||||
exit(ERROR_CODE);
|
||||
}
|
||||
```
|
||||
This type of error handling does definitely work, and i can see why it was implemented this way. although this type of error handling tends to lead to just stopping the program, or even worst not handling the error when it appears causing something to break later on. But we have had years to refine error handling and something more like rusts version of error handle just feels better for a modern time forcing the user to deal with error as they appear (yes i know you can just .unwrap and not give a shit but lets assume you have to deal with the error). But i will admit the initial learning curve of rusts robust error handle system is at times annoying. although i believe it to be one of the better implementations of it.
|
||||
|
||||
however this error handling system needs the use of generic typing since you dont know what OK type the error has. this would be an easy fix in the vast majority of languages just throw in you generic type syntax or dont even worry about it in dynamic languages. but c does not a syntax for creating generic type, so we must create our own using both the best and worst thing in the world the c pre processor. for its time c pre processor was an amazing way for implementing meta programming, however it has become a convoluted mess of string manipulation and type unsafety. some of this problems have been addressed over the years such as type unsafety by implementing the __auto_type operator for creating macros. however its still a dangerous beast that can fuck up your entire code with one little mistake that can take hours to fix.
|
||||
Although all of this are in contrast to the power the pre processor has and with this power comes little responsibility.
|
||||
|
||||
# implementation
|
||||
## defining the syntax
|
||||
so finally starting with how i implemented with result type as a pre processor macro. firstly the best thing to do is determent how you think the macro should be used, like you might make a really cool macro but people will be discouraged to use it if it makes no sense. however in every macro there will be some fuckary that needs to be done in order to make it work. so this is the kinda of syntax i wanted for the macro.
|
||||
```c
|
||||
|
||||
typedef enum numberAddingErrors_s{
|
||||
NUMBERS_NOT_EVEN,
|
||||
}numberAddingErrors_t;
|
||||
|
||||
typedef Result_t(int, numberAddingErrors_t, Result_type_t);
|
||||
|
||||
Result_type_t add_even_nums(int num1, int num2){
|
||||
if (num1%2 == 0 && num2%2 == 0){
|
||||
int num3 = num1+num2;
|
||||
Result_type_t_Ok(num3);
|
||||
}
|
||||
else {
|
||||
Result_type_t_error(NUMBERS_NOT_EVEN);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
int n1 = 4;
|
||||
int n2 = 5;
|
||||
Result_type_t answer = add_even_nums(n1, n2);
|
||||
int a = Result_type_t_unwrap(answer);
|
||||
printf("%d\n", a);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
so you first must define a error type here defined as `numberAddingErrors_t` as an enum. This is so i can have a ton of errors for each type but also doesnt need to be an enum it could be something like another struct so if something errors you know there is an error but it gives you the error struct just incase you can recover from this state.
|
||||
|
||||
the `typedef` defines what the error type is, the first argument of `Result_t` is the ok type, the next is the error type and finally is what the Result type should be called, since you will want more then one error type.
|
||||
|
||||
within the function that can error here `add_even_nums` that will error when both numbers arnt even. you can see a kind of more literal from of [hungarian notation](https://en.wikipedia.org/wiki/Hungarian_notation) where the type of the function prefixes the name of the function. this was done again to differentiate between each error type
|
||||
|
||||
## implementing the syntax
|
||||
finally the actually implementation of the macros the implement this
|
||||
```c
|
||||
#define Result_t_type(ok_type, error_type, type) \
|
||||
struct Result_t { \
|
||||
union Data_U{ok_type ok; error_type error;} data; \
|
||||
_Bool has_errored; \
|
||||
} type; \
|
||||
|
||||
#define Result_t_funcs(ok_type, error_type, type) \
|
||||
type type##_Ok(ok_type ok){return (type) {ok, 0};}; \
|
||||
type type##_Error(error_type error){return (type) {error, 1};}; \
|
||||
ok_type type##_unwrap(type result){if (!result.has_errored) {return result.data.ok;} exit(1);} \
|
||||
ok_type type##_unwrapor(type result, ok_type or){if (!result.has_errored) {return result.data.ok;} return or;}\
|
||||
|
||||
//you must call this with a type def to avoid complier checks
|
||||
#define Result_t(ok_type, error_type, type)\
|
||||
Result_t_type(ok_type, error_type, type)\
|
||||
Result_t_funcs(ok_type, error_type, type)\
|
||||
```
|
||||
something within this code you might not have seen before is the [##](https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html) operator. this operator basically combines two tokens in to one after macro expansion have been done.
|
||||
|
||||
the first macro defined here is the `Result_t_type` defined the structure of the result type. this uses a union of both the ok and error type to as these will more then likely be 2 different types and it would just be wasteful having space in the struct for each of the types at the same time as well as this data being mutually exclusive. the only other thing in here is a Bool to tell if the result has errored.
|
||||
|
||||
the second macro defined `Result_t_funcs` defines all the functions that the result type implements this also allows for more generalized functions to be added. there are 4 mostly self explanatory functions here.
|
||||
- `type type##_Ok`: use this if all your code didnt error
|
||||
- `type type##_Error` : use this if your code did error
|
||||
- `ok_type type##_unwrap` : assumes the states of the result is ok and gives the data, if not it exits the program with an error
|
||||
- `ok_type type##_unwrapor` : will attempt to unwrap the result type and if it has an error then it will default to the or arg
|
||||
|
||||
lastly the `Result_t` macro combines these 2 macros together. however you maybe be looking at this code and thinking you have defined `Result_t` in 2 places the struct and the macro. well due to the c pre processor not allowing recursive macros (pussies), the `Result_t` as defined within the struct does not actually get counted as a macro call.
|
||||
|
||||
# results
|
||||
|
||||
running this code with `n1 = 4` and `n2 = 2` gives the following output:
|
||||
```sh
|
||||
$ gcc temp.c && ./a.out
|
||||
6
|
||||
$
|
||||
```
|
||||
and then again running this code with `n1 = 4` and `n2 = 3` gives the following output:
|
||||
```sh
|
||||
$ gcc temp.c && ./a.out
|
||||
$
|
||||
```
|
||||
although not extensive tests it shows the basic principal of the code working
|
||||
|
||||
# conclusion
|
||||
this endeavor in to creating an appropriate replacement to the current c error handling works nicely. however this could benefit from some more extensive testing as well as some more unwrapping functions. for future improvement to this code i will be adding in more variations of the the unwrap function, changing the has_error to an error value so i can implement warns and other lesser errors.
|
||||
but overall it worked as i wanted it and i will probably continue to use it in the future.
|
||||
|
||||
the git repo link can be found [here](https://github.com/sirlilpanda/c-results-type).
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
title: 'First post'
|
||||
description: 'Lorem ipsum dolor sit amet'
|
||||
pubDate: 'Jul 08 2022'
|
||||
heroImage: '../../assets/blog-placeholder-3.jpg'
|
||||
---
|
||||
|
||||
|
||||
```python
|
||||
@something()
|
||||
def main() -> None:
|
||||
print("hello world")
|
||||
return;
|
||||
```
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vitae ultricies leo integer malesuada nunc vel risus commodo viverra. Adipiscing enim eu turpis egestas pretium. Euismod elementum nisi quis eleifend quam adipiscing. In hac habitasse platea dictumst vestibulum. Sagittis purus sit amet volutpat. Netus et malesuada fames ac turpis egestas. Eget magna fermentum iaculis eu non diam phasellus vestibulum lorem. Varius sit amet mattis vulputate enim. Habitasse platea dictumst quisque sagittis. Integer quis auctor elit sed vulputate mi. Dictumst quisque sagittis purus sit amet.
|
||||
|
||||
Morbi tristique senectus et netus. Id semper risus in hendrerit gravida rutrum quisque non tellus. Habitasse platea dictumst quisque sagittis purus sit amet. Tellus molestie nunc non blandit massa. Cursus vitae congue mauris rhoncus. Accumsan tortor posuere ac ut. Fringilla urna porttitor rhoncus dolor. Elit ullamcorper dignissim cras tincidunt lobortis. In cursus turpis massa tincidunt dui ut ornare lectus. Integer feugiat scelerisque varius morbi enim nunc. Bibendum neque egestas congue quisque egestas diam. Cras ornare arcu dui vivamus arcu felis bibendum. Dignissim suspendisse in est ante in nibh mauris. Sed tempus urna et pharetra pharetra massa massa ultricies mi.
|
||||
|
||||
Mollis nunc sed id semper risus in. Convallis a cras semper auctor neque. Diam sit amet nisl suscipit. Lacus viverra vitae congue eu consequat ac felis donec. Egestas integer eget aliquet nibh praesent tristique magna sit amet. Eget magna fermentum iaculis eu non diam. In vitae turpis massa sed elementum. Tristique et egestas quis ipsum suspendisse ultrices. Eget lorem dolor sed viverra ipsum. Vel turpis nunc eget lorem dolor sed viverra. Posuere ac ut consequat semper viverra nam. Laoreet suspendisse interdum consectetur libero id faucibus. Diam phasellus vestibulum lorem sed risus ultricies tristique. Rhoncus dolor purus non enim praesent elementum facilisis. Ultrices tincidunt arcu non sodales neque. Tempus egestas sed sed risus pretium quam vulputate. Viverra suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Fringilla urna porttitor rhoncus dolor purus non. Amet dictum sit amet justo donec enim.
|
||||
|
||||
Mattis ullamcorper velit sed ullamcorper morbi tincidunt. Tortor posuere ac ut consequat semper viverra. Tellus mauris a diam maecenas sed enim ut sem viverra. Venenatis urna cursus eget nunc scelerisque viverra mauris in. Arcu ac tortor dignissim convallis aenean et tortor at. Curabitur gravida arcu ac tortor dignissim convallis aenean et tortor. Egestas tellus rutrum tellus pellentesque eu. Fusce ut placerat orci nulla pellentesque dignissim enim sit amet. Ut enim blandit volutpat maecenas volutpat blandit aliquam etiam. Id donec ultrices tincidunt arcu. Id cursus metus aliquam eleifend mi.
|
||||
|
||||
Tempus quam pellentesque nec nam aliquam sem. Risus at ultrices mi tempus imperdiet. Id porta nibh venenatis cras sed felis eget velit. Ipsum a arcu cursus vitae. Facilisis magna etiam tempor orci eu lobortis elementum. Tincidunt dui ut ornare lectus sit. Quisque non tellus orci ac. Blandit libero volutpat sed cras. Nec tincidunt praesent semper feugiat nibh sed pulvinar proin gravida. Egestas integer eget aliquet nibh praesent tristique magna.
|
||||
147
src/content/blog/game-engine.md
Normal file
147
src/content/blog/game-engine.md
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
title: 'game engine'
|
||||
description: 'a little game engine i started to write after learning how GPUs actually work'
|
||||
pubDate: 'Jun 27 2024'
|
||||
heroImage: '../../assets/article_hero_images/game_engine.png'
|
||||
---
|
||||
|
||||
This game engine was developed for 2 main reasons, the first one was none of the game engines/frameworks on the market were that fast or were written in a horrible language like c++ or rust and wanting to create something myself. This was project was start after I finished my computer graphics course at uni. I wanted to explore more rendering techniques and just try to utilise all the knowledge I gathered from the course into a project.
|
||||
### If you want to skip to a specific part:
|
||||
|
||||
# Language choice
|
||||
This game engine was written in zig, why? Because:
|
||||
|
||||
- C requires me to use some build system like cmake or just straight up makefiles. But c does allow me to do the meta programming shenanigans that I like.
|
||||
|
||||
- Rust would require me to fight with the borrow checker the annoying rat, but does have a nice build system and great language features that would make it trivial to implement some of the features, but I felt like this project isn’t the type of thing that would benefit from rusts amazing error handling and memory safety
|
||||
|
||||
- C++, I have only one word for why I wont use it “overloading”
|
||||
|
||||
- Python, i have a lot of experience with this language but I believe it has little to no support for low level OpenGL calls, as well as being slow
|
||||
|
||||
- Java, as much as I have come to respect java for the language that it, I still despise OOP with a passion even if it makes complete sense for OpenGL
|
||||
|
||||
- Odin a very cool language for computer graphics with inbuilt quaternion types and matrixes, with amazing support for graphic libraries its just a little too new for me, having to compile it from scratch, but might be an idea to use in the future (but I still don’t like its use of the proc keyword for functions, a procedure is something completely different to a function)
|
||||
|
||||
So with why I did pick zig over basically every other language. Well the zig comptime keyword allows me to do the fun metaprogramming shenanigans that I love. As well as the zig having the best mascot (maybe except for V lang). zig also has some really cool features like allocators arnt global and you must pass them when every trying to add to the heap (great for embedded), it has oop in a sense but no inheritance (v tables arnt real and cant hurt me). As well as zig having quite honestly the best thing a very clean abstraction on SIMD instruction with the Vector type. Zig also has this amazing unit test system build into the language so you don’t have to have another dir called tests that just mirrors the main one, instead you can just write all your tests at the bottom of the file. All of these combined with a little personal bias made me picked zig.
|
||||
|
||||
# Windowing and graphics libraries
|
||||
Zig maybe be a really cool language with a great build system but it does currently lack something, a vast mature set of libraries (and documentation in some cases). This meant that only really low-level things were exposed, since zig integration with the c ABI allows for the large library projects like glfw, sdl, etc. to be ported easy. For this project I choose OpenGL as that is what my computer graphics course used and that being the one I had experience in, although I did also look at vulkan and decided not to due to the shear quantity of boiler plate and some of the strange dynamic linking with function pointers and wrappers that vulkan-zig uses. However in the future I would like to try vulkan again so I can abuse more threads in my program as well as try a openXR for VR applications.
|
||||
For this project I originally wanted to use SDL 2 for it being used within valves games as well as having the bare minimum of calls to implement things, so I could write my own subsystems. However when trying to get the opengl context to use with SDL2, it would not work no matter what I did, therefor I reverted back to GLFW, still has all the same features as SDL2 but sightly more bloat as I see it.
|
||||
# The math
|
||||
So after starting rendering the obligatory triangle I realized that, I now need matrix/vector math to actually start to render 3D objects. Luckly I already had a file full of vector math from one of my previous projects writing a ray tracer in zig.
|
||||
## Vectors
|
||||
This vector math was all done the normal way with struct that as 3 values, but zig has the inbuilt vector type that could easily be handle all these vector functions.
|
||||
|
||||
```ts
|
||||
pub fn Vec(comptime length: comptime_int) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
vec: @Vector(length, f32),
|
||||
}
|
||||
```
|
||||
Snippet of the vector type
|
||||
|
||||
This is a very thin abstraction the on the vector type that implements all your common operations, Add, sub, mul, div, norm, scale, etc. as well as a possibility to add 3 more for raytracing applications reflect, refract and a less common but really cool one reflectance for computing how things would bounce off water on a large scale.
|
||||
Comparing this new fancy vectorised one to the old slow vector struct running each test 200 times to get a clearer image of how long it took.
|
||||
|
||||

|
||||
Graph of time comparison
|
||||
|
||||
we can see all the functions are greatly faster other than the obvious ones. However min and dot were pretty similar. my guess for this is it got optimised down to near enough the same code. The len functions are the same but that’s to be expected. now on to the outlier’s norm and scale. These were computed on a vector length of 3, I believe this played a keyfactor within why they were slow.
|
||||
|
||||
```ts
|
||||
pub fn mag(a: Self) f32 {
|
||||
return @sqrt(@reduce(.Add, a.vec * a.vec));
|
||||
}
|
||||
|
||||
pub fn norm(a: Self) Self {
|
||||
return Self{ .vec = a.vec / number(a.mag()).vec };
|
||||
}
|
||||
|
||||
pub fn scale(a: Self, b: f32) Self {
|
||||
return Self{ .vec = a.vec * number(b).vec };
|
||||
}
|
||||
```
|
||||
Vectorized norm and scale code
|
||||
|
||||
```ts
|
||||
pub fn scale(a: Self, b: T) Self {
|
||||
return Self{
|
||||
.x = a.x * b,
|
||||
.y = a.y * b,
|
||||
.z = a.z * b,
|
||||
};
|
||||
}
|
||||
// len here is mag
|
||||
pub fn norm(a: Self) Self {
|
||||
const den: T = len(a);
|
||||
return a.scale(1 / den);
|
||||
}
|
||||
|
||||
```
|
||||
Non Vectorized code
|
||||
|
||||
as you can see in the vectorize code within norm, the first step that is done is to compute the magnitude of the vector, this as we saw in the graph took about the same time as the normal magnitude code, but then it is required to do another vector operation on top of that, as well needing to creating an entirely new vector from this number inorder to scale the rest, I believe this creation of the new vector is causing the largest slow down within this code. The scale function also suffers the same problem if I have to guess, creating a new vector within the code before doing the operation.
|
||||
However, this vector math code is still vastly faster than the original slow vector math, as well as being able to take full advantage of SIMD vectors, for when I want create vectors of arbitrary length
|
||||
|
||||
## Matrix math
|
||||
Now on to the good stuff, vector math is easy and only really has the basic operations to it, but matrix math has the good stuff in it, and the most potential for improvement.
|
||||
The matrix struct in the code much like the vector code, is a very thin wrapper on a vector. Although my matrix code differs from other peoples implementation as instead of using an array of vector for either each row or col I choose to use 1 long vector, this was done to make the addition of matrixes to each other simpler as well as giving rise to some interesting code.
|
||||
```ts
|
||||
pub const Mat3x3 = struct {
|
||||
const Self = @This();
|
||||
vec: @Vector(9, f32),
|
||||
}
|
||||
```
|
||||
Snippet of the matrix type
|
||||
|
||||
The 3x3 matrix type implements most of the common operations, such as identity, mul and transpose. But as this is a game engine, we don’t care that much about the 3x3 matrix we care more about the 4x4 matrixes. These matrixes are implemented the same way but with a vector of 16 instead of 9. Now I will be explaining each function as my method of implementing them is unique and fully abuses the power of the abstraction of SIMD vectors.
|
||||
|
||||
### Matrix multiplication
|
||||
Lets start out with the bread and butter of a matrix math library, the most common operation and therefor the one that needs to be the fastest. Most implementation of this rely on the matrix already being separated in to rows/col for easy indexing for calculating the dot product like seen in the zig mach engine, but as my matrix only has one vector, this wasn’t going work. Therefor after some thinking my idea was to compute the all the multiplication and additions of the dot products in one go, this technique would allow the code to use larger SIMD vector like seen in SSE4 which my cpu supports (no I did not know this at the time).
|
||||
So the first step of the matrix mul is to compute the dot product of each row vector from matrix A and with each col vector from matrix B, if it is within the form of
|
||||
$$
|
||||
C = A*B
|
||||
$$
|
||||
Now if we look at the first item of C i.e. C0, 0
|
||||
|
||||
C[0,0]=A[0,0]B[0,0]+A[0,1]B[1,0]+A[0,2]B[0,2]+A[0,3]B[0,3]
|
||||
|
||||
And if we look at C1, 0: :
|
||||
C[1,0]=A[1,0]B[0,0]+A[1,1]B[1,0]+A[1,2]B[0,2]+A[1,3]B[0,3]
|
||||
|
||||
We can see that the elements of B do not change, so to calculate the first col of C we can copy B 4 times for each row of A which if we write it out in a slight nicer method, and by method I mean using a table:
|
||||
|
||||
| reg[0] | reg[1] | reg[2] | reg[3] | reg[4] | ... |
|
||||
| --------- | --------- | --------- | --------- | --------- | --- |
|
||||
| A[0, 0] | A[0, 1] | A[0, 2] | A[0, 3] | A[1, 0] | ... |
|
||||
| B[0, 0] | B[0, 1] | B[0, 2] | B[0, 3] | B[0, 0] | ... |
|
||||
| C[0,0]_0 | C[0,0]_1 | C[0,0]_2 | C[0,0]_3 | C[1,0]_0 | ... |
|
||||
|
||||
And the dot product of the C at the index [i, j] being the:
|
||||
$$
|
||||
C\left[i,j\right]=\Sigma C\left[i,j\right]_w
|
||||
$$
|
||||
|
||||
Or rewriting it in the form as before:
|
||||
C[0,0]=A[0,0]B[0,0]+A[0,1]B[1,0]+A[0,2]B[0,2]+A[0,3]B[0,3] = C[0,0]<sub>0</sub> + C[0,0]<sub>1</sub> + C[0,0]<sub>2</sub> + C[0,0]<sub>3</sub>
|
||||
|
||||
For sake of space I wont write out the full thing since its 64 items longs but you get the drift, just you can expand out all the multiplication of all the dot products within the matrix mul, and this is how you get the 2 large 64 long masks as you can see in the mat mul function [here].
|
||||
|
||||
now that we have done all the multiplications we now actually need to do the summing of all these dot products. A sane person may extract them out then reduce them in their own vectors, however I am not sane, and since we already have them in the vector format we may as well keep them there. Therefor the dot product is done in vaguely the same way as the mul step, all at once or as close as we can. So this time we align all C\left[i,j\right]_w indexes to each other so in 4 different vectors C[0, 0]0 , C[0, 0]1 , C[0, 0]2 , C[0, 0]3 will all be at index 0 in all 4 vectors, looking at this using the same table method as before:
|
||||
|
||||
| | | ... |
|
||||
| --- | --- | --- |
|
||||
| C[0,0]_0 | C[1,0]_0 | ...
|
||||
| C[0,0]_1 | C[1,0]_1 | ...
|
||||
| C[0,0]_2 | C[1,0]_2 | ...
|
||||
| C[0,0]_3 | C[1,0]_3 | ...
|
||||
|
||||
then you just add all 4 of these vectors together and get all the dot products of all the values in the right indexes.
|
||||
Now this idea does sound slightly on the insane side, but comparing it with a conventional matrix mul as can be seen below we can see that this is ~50x faster than the normal matrix mul. However I believe the vast majority of this speed up is from the fact my CPU has [sse4] which will allow for the full computation of the 64 mul step to be done at once rather than break it up like cpus that don’t have the full sse instructing set will do.
|
||||

|
||||
|
||||
|
||||
|
||||
I am still writing more of this
|
||||
588
src/content/blog/kicad-tempate-repo.md
Normal file
588
src/content/blog/kicad-tempate-repo.md
Normal file
@@ -0,0 +1,588 @@
|
||||
---
|
||||
title: 'kicad project template'
|
||||
description: 'a repo for easily creating repos with kicad in it'
|
||||
pubDate: 'Nov 05 2024'
|
||||
heroImage: '../../assets/article_hero_images/kicad-tempate-repo.png'
|
||||
---
|
||||
|
||||
#### table of content
|
||||
- [intro](#intro)
|
||||
- [creating a project from this repo](#creating-a-project-from-this-repo)
|
||||
- [configuring repo settings](#configuring-repo-settings)
|
||||
- [project structure](#project-structure)
|
||||
- [the workflow](#the-workflow)
|
||||
- [set up project](set-up-project)
|
||||
- [matrix setup](matrix-setup)
|
||||
- [ERC and DRC reports](ERC-and-DRC-reports)
|
||||
- [Production files](Production-files)
|
||||
- [updating the readme](updating-the-readme)
|
||||
- [uploading production files](uploading-production-files)
|
||||
- [improvements and future work](#improvements-and-future-work)
|
||||
|
||||
# intro
|
||||
when creating hardware project with kicad i alway found my self uploading the bare kicad project in a folder called `hardware` with a `.gitignore` and most of the time not even a read me. this became annoying if i wanted to show people what i have created, or even have them look over the schematic since i would forget to generate one or it would be out of date. These problems could be over come with more discipline when creating hardware project, but im *lazy* and will forget. Therefor i decided to do the correct developer approach spend 30 hours on automating a task that takes 30 seconds. Next is how are these automation going to be done, i could just make a script that would run on a project and do all of this, but that sounds like more effort and something i will just forget to do.
|
||||
|
||||
So instead i decided on doing the insane thing of creating this as a [template repo](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository) in github and having a [github action](https://docs.github.com/en/actions) to handle all of this. this doesn't actually sound that insane but templates repo only feature is you can create a new repo with all these files already in it. This is useful until you realise that means the kicad project name will be the same for all of these templates and that just doesnt fly with me. Imagine opening recent projects and seeing 10 projects called `hardware-project`. So instead i can use a github action on creation to rename the project to what ever the repo is called, [i get back to this nightmare later](#set-up-project). This now create the new problem of the github action now needing to be dynamic something that people think is a bad idea, but im to stubborn to listen to them and my music is too loud to hear them.
|
||||
|
||||
so now that i have established the vauge overview of how i set up this template, its time to introduce the things that i automated the production of. Using the [kicad-cli](https://docs.kicad.org/8.0/en/cli/cli.html) as this would be the only way i could automate anything with this, it allows me to produce these outputs:
|
||||
|
||||
- electronic rules check
|
||||
|
||||
- design rules check
|
||||
|
||||
- schematic pdf
|
||||
|
||||
- schematic BOM
|
||||
|
||||
- pcb gerbers
|
||||
|
||||
These are the most important things and things i forget to do, and so do most of you. The rules checks are especially cool because this allows the workflow to fail if your pcb doesnt pass these, showing you the nice big red fail on your [workflow badge](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/adding-a-workflow-status-badge), and warning others not to trust your design yet. so lets walk through the setup of one of these project, with some explanations of why i did what i did, because somethings maybe slightly annoying but i did it.
|
||||
|
||||
# creating a project from this [repo](https://github.com/sirlilpanda/kicad-project-template)
|
||||
has linked in the heading is a link to the template repo im talking about, within in is [project setup](https://github.com/sirlilpanda/kicad-project-template?tab=readme-ov-file#project-setup) section, but i will rehash what is going on in it here for more clarity of what is happening.
|
||||
|
||||
creating a new template from a repo is quite simple:
|
||||
|
||||
1. click on the button in the top right hand corner called `use this template`
|
||||

|
||||
|
||||
2. when the drop down menu appears click on `create a new repository`
|
||||

|
||||
|
||||
3. once you click you will be brought to this page, note that the repo name you choose here will be the name that you kicad project is called. This was the only nice place i could get you to put in the name of what you are calling your project.
|
||||

|
||||
|
||||
due to the workflow committing to the repo you then have to configure some repo settings
|
||||
|
||||
# configuring repo settings
|
||||
|
||||
once you have created a new repo with the template, you ill have to configure the github action settings. This is because the workflow will need to commit to the repo and this isnt enabled by default for good reasons. therefor to configure this follow these steps:
|
||||
|
||||
1. open the repo setting
|
||||

|
||||
|
||||
2. then head in to actions : General
|
||||

|
||||
|
||||
3. then scroll down to workflow permissions and ensure that both
|
||||
`Read and write permissions` and `Allow GitHub Actions to create and approve pull requests` are ticked as seen below, honestly i dont know if `Allow GitHub Actions to create and approve pull requests` needs to be ticked and i dont care to untick it.
|
||||

|
||||
|
||||
now once this is done i suggest that you re run the workflow before starting to create your hardware.
|
||||
|
||||
# project structure
|
||||
|
||||
yeah yeah yeah, i know you want me to talk about the workflow and how it works, but first we have to look at what the fuck you actually just created. Below is an outline of the project structure but dont worry, unless you want to do something strange and change the workflow, or make new report templates you will never have to go in to the `.github/` folder. So lets actually explain what each section does, starting with the `.github/` since its at the top, and will be short as this information will be expanded on in the [workflow section](#the-workflow).
|
||||
```txt
|
||||
kicad-project-template
|
||||
┠ .github/
|
||||
┃ ┠ report_processing/
|
||||
┃ ┃ ┗ ...
|
||||
┃ ┠ report_templates/
|
||||
┃ ┃ ┗ ...
|
||||
┃ ┠ workflows/
|
||||
┃ ┠ get_pcb_name.py
|
||||
┃ ┗ rename.py
|
||||
┠ Hardware
|
||||
┃ ┠ {{project_name}}_DOCS
|
||||
┃ ┃ ┠ BOM/
|
||||
┃ ┃ ┃ ┠ {{project_name}}_bill_of_materials.csv
|
||||
┃ ┃ ┃ ┗ {{project_name}}_bom_report.md
|
||||
┃ ┃ ┗ {{project_name}}_schematic.pdf
|
||||
┃ ┠ {{project_name}}_PCB
|
||||
┃ ┃ ┗ {{project_name}}_grbr.zip
|
||||
┃ ┗ {{project_name}}_PROJECT
|
||||
┃ ┗ ...
|
||||
┠ res/
|
||||
┠ project_settings.yaml
|
||||
┠ .gitignore
|
||||
┗ README.md
|
||||
```
|
||||
### .github/
|
||||
within this folder contains all the administrative data and scripts like the [report templates](https://github.com/sirlilpanda/kicad-project-template/tree/main/.github/report_templates) and the [report processing script](https://github.com/sirlilpanda/kicad-project-template/tree/main/.github/report_processing). In here is also the github workflow in the workflow folder.
|
||||
|
||||
#### [report templates](https://github.com/sirlilpanda/kicad-project-template/tree/main/.github/report_templates)
|
||||
These are templates for automatically creating reports within the other actions like, this should be fairly self explanatory.
|
||||
- [`bom_report_template.mustache`](https://github.com/sirlilpanda/kicad-project-template/blob/main/.github/report_templates/bom_report_template.mustache)
|
||||
this template is used for creating the bom reports as can be seen in the [production file section](#production-files)
|
||||
|
||||
- [`drc_report_template.mustache`](https://github.com/sirlilpanda/kicad-project-template/blob/main/.github/report_templates/drc_report_template.mustache)
|
||||
this template is used for creating the step summary reports as can be seen in the [erc/drc report section](#erc-and-drc-reports)
|
||||
|
||||
- [`erc_report_template.mustache`](https://github.com/sirlilpanda/kicad-project-template/blob/main/.github/report_templates/erc_report_template.mustache)
|
||||
this template is used for creating the step summary reports as can be seen in the [erc/drc report section](#erc-and-drc-reports)
|
||||
|
||||
- [`readme.mustache`](https://github.com/sirlilpanda/kicad-project-template/blob/main/.github/report_templates/readme.mustache)
|
||||
the hash for this report has already been discussed in the [update readme section](#updating-the-readme)
|
||||
|
||||
#### [report processing script](https://github.com/sirlilpanda/kicad-project-template/tree/main/.github/report_processing)
|
||||
these are post processing scripts for all the outputs to convert them in to the desired hash for the reports. I may add more information to this if people request it.
|
||||
|
||||
- `process_bom_files.py`
|
||||
|
||||
- `process_drc_json.py`
|
||||
|
||||
- `process_erc_json.py`
|
||||
|
||||
- `process_json_reports.py`
|
||||
|
||||
- `process_output_files.py`
|
||||
|
||||
- `violation.py`: a common class used between the drc and erc reports as this shares the same schema
|
||||
|
||||
`get_pcb_name.py` this is the script that gets all the kicad project names from the project setting to use for the workflow
|
||||
|
||||
`rename.py` this script is used to rename the project on start up.
|
||||
|
||||
### Hardware
|
||||
this is the dir where all the production files are committed too. this is also where the kicad projects are stored.
|
||||
|
||||
#### {{project_name}}_DOCS
|
||||
this folder is where are the bom files go as well as the schematics this is also a good location for any other items of hardware documenting that you many want to include such as usage guide or build guides.
|
||||
##### BOM
|
||||
within here all the bill of materials outputs such as the `*.csv` and the bom reports.
|
||||
|
||||
#### {{project_name}}_PCB
|
||||
this is where the pcb gerbers are outputted too and possibly the `*.vrml` in the future.
|
||||
|
||||
#### {{project_name}}_PROJECT
|
||||
this is where the actual main kicad project is stored.
|
||||
|
||||
#### {{sub_pcb_name}}_PROJECT
|
||||
if you want to add another project do it with this syntax and add it to the project settings under `sub_pcb_names` all you need to do is a `-{{sub_pcb_name}}` under the `sub_pcb_names` and it will be added to the workflow, if you dont want it in the workflow then you can just remove the name from this list.
|
||||
|
||||
### project settings
|
||||
|
||||
these settings are defined within a [yaml](https://yaml.org/spec/) file as these are actually made to be human readable unlike [json](https://www.rfc-editor.org/rfc/rfc8259) which is just a funny looking javascript object in a string. currently there are few project settings although more to come. you can checkout the settings here with there [descriptions](https://github.com/sirlilpanda/kicad-project-template/blob/main/project_settings.yaml). or here is a small table ripped from the read me with an overview on them.
|
||||
|
||||
there are currently very few project settings that can be changed these are:
|
||||
|
||||
- needs_setup : </br>
|
||||
a flag to tell the setup action if the project has been set up, this is set by the action step so if you want to rerun the setup step then just set this to `true` again
|
||||
|
||||
- project_name : </br>
|
||||
the name of the project, this will be set to the name of the of repo
|
||||
|
||||
- production_formats : </br>
|
||||
the output production format for the PCBs, i have yet to actually implement this as i doubt no one will use it
|
||||
|
||||
- dynamic_read_me : </br>
|
||||
allow the readme to be updated using the given template
|
||||
|
||||
- sub_pcb_names : </br>
|
||||
the names of all other kicad projects within the repo
|
||||
|
||||
- bom_template_path : </br>
|
||||
the template used to write the bom report files
|
||||
|
||||
- erc_report_template_path : </br>
|
||||
the template used to write the erc report files
|
||||
|
||||
- drc_report_template_path : </br>
|
||||
the template used to write the drc report files
|
||||
|
||||
- readme_template_path : </br>
|
||||
the template used to write projects readme
|
||||
|
||||
|
||||
### res
|
||||
this is just where all the readme images are stored
|
||||
|
||||
|
||||
### .gitignore
|
||||
a barely modified `.gitignore` for the default [kicad one](https://github.com/github/gitignore/blob/main/KiCad.gitignore)
|
||||
all it has extra in it, is to ignore `__pycache__/` that might be created by one of the python scripts.
|
||||
|
||||
# the workflow
|
||||
|
||||
the workflow as can be seen below is a surprisingly complex creature, partly because i believe workflows arnt meant to be used in this manner but here we are. This workflow will run on a push to any of these 3 branches, [`main`, `master`, `workflow_testing`], main and master because thats what people tend to called their primary branch and `workflow_testing` because i needed a branch for testing the workflow. This will more than likely be changed in the future to be all branches. So what does this workflow actually do:
|
||||
|
||||
1. first its checked to see if the [project has be setup](#set-up-project) and if not it will rename all the files and commit them back to the repo.
|
||||
|
||||
2. Next the workflow will create a list of the kicad projects that are needed for the next steps. Yes you read that right kicad *projects*, multiple this was done because more and more project now tend to have another backpack or shield board for faster prototyping and testing.
|
||||
|
||||
3. After this list has been created the workflow will run an ERC, DRC and create all the production files needed.
|
||||
|
||||
4. once all these checks and production files have been created then the readme updater runs, yes it will auto update the readme of the project, why because you are *lazy* and will forget to do this. it also shows the status of the ERC and DRC
|
||||
|
||||
5. finally it commits all the production files back to the repo
|
||||
|
||||

|
||||
|
||||
## set up project
|
||||
we will first discuss the project setup step. This step renames the project to what ever the repo name is, as this is most likely what you were going to call your kicad project anyway. This step has to be run everytime the workflow is started, why? you may ask, because the trigger that would run on the creation of a repo doesnt exist, but do you know what does trigger on the creation of a repo the `push` trigger. So this setup action ends up running each time, but dont worry because it only runs about 2 steps before it checks [project settings](#project-settings) and sees that the project has already been setup and all later steps are skipped(yes you might ask why doesn't the action just finish early with a success, well because they havent implemented this). So now lets go through the steps that this action does:
|
||||
|
||||
1. `checkout`
|
||||
this pulls down the repo
|
||||
|
||||
2. `read yaml file`
|
||||
this read the project settings and outputs an object of the settings
|
||||
|
||||
3. `check setup`
|
||||
this spits out the state of the `has_been_set_up` setting
|
||||
|
||||
4. `Setting up Python`
|
||||
i dont need to explain this
|
||||
|
||||
5. `installing requirements`
|
||||
installing the requirements that the rename script needs mainly `ruamel.yaml` a yaml parsing library that allow for the comments to stay while changing the values
|
||||
|
||||
6. `rename project if setup has not been completed`
|
||||
renames the project using the repo name, also sets the `has_been_set_up` setting to true and the `project_name` setting to the name of the repo. This is done by using the `rename.py` script, why did i use python for this because python funnily enough is a scripting language.
|
||||
|
||||
7. `commits production files`
|
||||
this just commits the changes back to the repo
|
||||
|
||||
## matrix setup
|
||||
|
||||
As much as i would love this to be setting up a virtual world, it sadly is not*. This step reads the project settings and get the project name as well as all the sub PCB name. This jobs main purpose is to create and output a list that can be used to run the [ERC](#erc-and-drc-reports), [DRC](#erc-and-drc-reports), and [production files](#production-files) jobs on the multiple projects. As you can see this doesnt do alot but ill still outline the steps for consistency.
|
||||
|
||||
1. `checkout`
|
||||
it needs to get the project settings somehow
|
||||
|
||||
2. `Setting up Python`
|
||||
sets up python
|
||||
|
||||
3. `installing requirements`
|
||||
installs `pyyaml` for reading the project requirement, this was done as its nicer that `ruamel.yaml` which i have strong opinions about.
|
||||
|
||||
4. `get kicad project names`
|
||||
gets all the name of the projects in the project settings and throw it in to the `$GITHUB_OUTPUT` for the next jobs to use, I could have probably just done some bash trickery with this, but this was nicer and more people can modify it if they want to.
|
||||
|
||||
|
||||
\* it kinda does since github actions actually run in a fully virtualised environment so it kinda is a virtual world.
|
||||
|
||||
## ERC and DRC reports
|
||||
|
||||
Im lumping these in to the same section as they do basically the same thing, just on different parts of the project. this action runs the DRC/ERC on the kicad project and creates a report on it, that nicely gets outputted in the actions tab as a [job summary](https://github.blog/news-insights/product-news/supercharging-github-actions-with-job-summaries/) as can be seen below.
|
||||

|
||||
|
||||
yes i know the emojis are a nice touch, it looked to boring without colour. however you might also be asking yourself why dont these look like the reports i get out of kicad when i export my reports, or if you havent save the DRC or ERC reports from kicad, below is an example of them. As you can see perfectly readable although they are not the prettiest thing out there. therefor i decide to do some processing to them instead.
|
||||
```txt
|
||||
ERC report (2024-11-06T11:14:43+1300, Encoding UTF8)
|
||||
|
||||
***** Sheet /
|
||||
[endpoint_off_grid]: Symbol pin or wire end off connection grid
|
||||
; warning
|
||||
@(97.7900 mm, 98.4250 mm): Horizontal Wire, length 9.5250 mm
|
||||
[endpoint_off_grid]: Symbol pin or wire end off connection grid
|
||||
; warning
|
||||
@(97.7900 mm, 95.8850 mm): Horizontal Wire, length 9.5250 mm
|
||||
...
|
||||
```
|
||||
and like this for the design rules check:
|
||||
```txt
|
||||
** Drc report for bluetooth_dac.kicad_pcb **
|
||||
** Created on 2024-11-06T11:17:14+1300 **
|
||||
|
||||
** Found 88 DRC violations **
|
||||
[track_dangling]: Track has unconnected end
|
||||
Local override; warning
|
||||
@(212.8800 mm, 73.3500 mm): Track [+3.3V] on F.Cu, length 1.1900 mm
|
||||
[via_dangling]: Via is not connected or connected on only one layer
|
||||
Local override; warning
|
||||
@(195.6927 mm, 80.6449 mm): Via [/SPI_CS] on F.Cu - B.Cu
|
||||
...
|
||||
```
|
||||
before you ask no i did not process the raw txt reports that would be insane and not something i would do just dont look back to commit [`3632978`](https://github.com/sirlilpanda/kicad-project-template/commit/3632978c9c427af78dec9501c375f784498f74b4), that was not me and you have no proof. However this was because the output format of the report could be selected. This allows the reports to be spat out as json rather than plain text. The schemas for these json file are found at the top of the file, but these are out of date and no longer go to anywhere. However they do exists within the kicad repo and they can be seen here, [DRC schema](https://gitlab.com/kicad/code/kicad/-/blob/master/resources/schemas/drc.v1.json) [ERC schema](https://gitlab.com/kicad/code/kicad/-/blob/master/resources/schemas/erc.v1.json). Although there is a problem with the ERC report output as json, the position doesnt actually output as as its said mm, but instead outputs in decimeter. This is a simple fix if the file is an erc report just multiply the pos by 100, but annoying to find out.
|
||||
|
||||
Now to actually create these reports i used a templateting language called [mustache](https://mustache.github.io/). This allows for the creation of a template report that can then be filled in with what mustache calls a hash, just a funny looking bit of json that will fill out these reports. These template reports can be found funnily enough in the [`report_templates/`](https://github.com/sirlilpanda/kicad-project-template/tree/main/.github/report_templates) directory, under `erc_report_template.mustache` and `drc_report_template.mustache`. When you open one of these reports the hash schema can be found at the top. However you might notice that these hashs arent the same json as the report json. Well as you guess i did post processing on output reports to create the hash i wanted. why you may ask it created a slightly nicer and easier to read. The original didnt have things like total number of errors per sheets, and all the warns and errors were meshed together in one large array which made it harder to read, because when the ERC fails you want to look of the errors first not the sieve through all the warns until you find the error you want. It also allows things for things like the markdown version of the name to be computed allowing for fast traversal of the report. So lets look at one of these reports when the rules check has failed.
|
||||
|
||||

|
||||
|
||||
at the top the title of the report can be seen with a time stamp of when it was made. just below that we have the number of the total warns and errors, and below that is a break down of them per sheet in this case for the ERC report. The sheet name in this table is a link to the section for that. After that we can see the section that contains the sheet with the title being the sheet name, and 2 sub heading of `warns` and `errors`. Within these sub sections a list of the violation that cause this, the violation name, what symbols are causing it and its position on the sheet.
|
||||
|
||||
And just encase it wasnt clear enough as these are templates you too can create your own report with either the hash that is provided or you can modify the [report processing scripts](https://github.com/sirlilpanda/kicad-project-template/tree/main/.github/report_processing) to create your own hash to make your own custom reports.
|
||||
|
||||
lastly this step also uploads an artifact this artifact is called `{{project name}}_erc.json` or `{{project name}}_drc.json` which ever one is being run, which is used for the updating [readme step](#updating-the-readme). This artifact is a simple piece of json that can been seen below:
|
||||
```json
|
||||
{
|
||||
"passing_erc":"true",
|
||||
"project_name":"template",
|
||||
"erc_summary_link":"https://github.com/sirlilpanda/kicad-project-template/actions/runs/11885615488/attempts/1#summary-33115574474"
|
||||
}
|
||||
```
|
||||
this just contains simple information like whether the erc/drc passed, the name of which kicad project was being processed and lastly a link to the summary card that it produced. This data is uploaded as an artifact since its the only way to get around matrixes not having a nice way to output.
|
||||
|
||||
And now after that, here is what the steps of the work flow does
|
||||
|
||||
1. `checkout`
|
||||
gotta run the erc/drc on something
|
||||
|
||||
2. `read yaml file`
|
||||
needs the project settings for the report templates
|
||||
|
||||
3. `sets up python`
|
||||
python is used here for processing the template reports and putting in the hash to create the new reports
|
||||
|
||||
4. `install requirements`
|
||||
cant run the python script if you dont have the requirements
|
||||
|
||||
5. `run ERC/DRC`
|
||||
this uses an made by [Spark Engineering](https://github.com/sparkengineering/kicad-action) for processing running the ERC or DRC
|
||||
|
||||
6. `creates ERC/DRC report in markdown`
|
||||
computes the new hash from the json output and creates the new report using the mustache template
|
||||
|
||||
7. `upload report summary`
|
||||
uploads the report to github as a [job summary](https://github.blog/news-insights/product-news/supercharging-github-actions-with-job-summaries/)
|
||||
|
||||
8. `get summary url`
|
||||
using an action to get the link to the summary that was just created
|
||||
|
||||
9. `prints summary url`
|
||||
this exists mostly for debugging
|
||||
|
||||
10. `create files to upload`
|
||||
this creates the json that was perviously discussed
|
||||
|
||||
11. `upload data for readme`
|
||||
this uploads the json that was perviously discussed
|
||||
|
||||
## Production files
|
||||
|
||||
The production files, the stuff that should always be in repo. This stuff is also things you are most likely to forget to add to the repo, these are:
|
||||
|
||||
- the [bom](https://github.com/sirlilpanda/kicad-project-template/blob/main/Hardware/template_DOCS/BOM/template_bill_of_materials.csv) so people know what is needed for the project
|
||||
|
||||
- a [bom report](https://github.com/sirlilpanda/kicad-project-template/blob/main/Hardware/template_DOCS/BOM/template_bom_report.md) for if you want to do some post processing to the bom;
|
||||
|
||||
- [schematic pdf](https://github.com/sirlilpanda/kicad-project-template/blob/main/Hardware/template_DOCS/template_schematic.pdf) so people can judge your design faster
|
||||
|
||||
- [gerbers](https://github.com/sirlilpanda/kicad-project-template/blob/main/Hardware/template_PCB/template_grbr.zip) for if someone doesnt know how kicad works but still wants to make your project.
|
||||
|
||||
The only odd thing this job does is creates a [bom report](https://github.com/sirlilpanda/kicad-project-template/blob/main/Hardware/template_DOCS/BOM/template_bom_report.md), why? so if you want to add an api call for showing how much your project will cost. I have yet to implement anything like this but you could implement and API from [mouser](https://nz.mouser.com/api-search/), [octopart](https://octopart.com/business/api/v4/api-transition) or [digikey](https://www.digikey.co.nz/en/resources/api-solutions). If you are going to do this i would suggest digikey or mouser as octopart believes extorsion is a fair cost for api usage. Just like the [ERC and DRC reports](#erc-and-drc-reports) the bom report uses a mustache template as well as a very [basic processing script](https://github.com/sirlilpanda/kicad-project-template/blob/main/.github/report_processing/process_bom_files.py).
|
||||
|
||||
All these production files, the bom, bom report, schematic pdf and gerbers all get uploaded as an artifact, this is done as there is no way to collect all the outputs of a matrix-ed workflow, therefor this is the workaround.
|
||||
The production file job also uploads some json as can be seen below. The json mainly contains links to the generated file as well as the project name for identification from which matrixed workflow it came from. further reasoning on why these links are created will discussed in the [readme](#updating-the-readme) section.
|
||||
```json
|
||||
{
|
||||
"project_name" : "string",
|
||||
"project_link" : "link",
|
||||
"schematic_link" : "link",
|
||||
"gerber_link" : "link",
|
||||
"bom_report_link" : "link",
|
||||
"bom_csv_link" : "link"
|
||||
}
|
||||
```
|
||||
|
||||
And now after that, here is what the steps of the workflow does
|
||||
|
||||
1. `checkout`
|
||||
need to create the production files from something
|
||||
|
||||
2. `read yaml file`
|
||||
this is needed to get the `project_name` for creating the right paths
|
||||
|
||||
3. `setting up python`
|
||||
this is needed for processing the BOM report
|
||||
|
||||
4. `installing requirements`
|
||||
cant run the python script if you dont have the requirements
|
||||
|
||||
5. `export production files`
|
||||
this creates 3 files, `sch.pdf`, `bom.csv`, `gbr.zip`schematic pdf, gerbers and bom
|
||||
|
||||
6. `moving production files`
|
||||
this moves the outputted files to the correct location in the repo and renamed to a more descriptive name
|
||||
</br> `sch.pdf` -> `{{matrix.project_name}}_schematic.pdf`
|
||||
</br> `bom.csv` -> `{{matrix.project_name}}_bill_of_materials.csv`
|
||||
</br> `gbr.zip` -> `{{matrix.project_name}}_grbr.zip`
|
||||
|
||||
7. `creating BOM report in markdown`
|
||||
this creates the actual bom report that has the name `{{matrix.project_name}}_bom_report.md`
|
||||
|
||||
8. `upload report`
|
||||
this uploads all artifact files
|
||||
</br> \- `{{matrix.project_name}}_schematic.pdf`
|
||||
</br> \- `{{matrix.project_name}}_bill_of_materials.csv`
|
||||
</br> \- `{{matrix.project_name}}_grbr.zip`
|
||||
</br> \- `{{matrix.project_name}}_bom_report.md`
|
||||
|
||||
9. `data for readme updating`
|
||||
this create the json file as perviously discussed
|
||||
|
||||
10. `upload report`
|
||||
this uploads the json file that was created as `{{matrix.project_name}}_project.json`
|
||||
|
||||
## updating the readme
|
||||
|
||||
This is the coolest part of the workflow, this allows the repo to auto update the readme through yet again use of a [mustache template](https://github.com/sirlilpanda/kicad-project-template/blob/main/.github/report_templates/readme.mustache), why did i do this because its pretty cool and it means i dont have to do it. However you can disable this feature by setting the [project setting](#project-settings) `dynamic_read_me` to `false`. The readme hash has a lot of information that can be used to update the readme and embed in to it. currently within the readme hash there is:
|
||||
|
||||
- `badge`: </br>
|
||||
This action badge that can be seen below, in fact this is the exact action badge of the project and will auto update with the action. This badge will also update depending on the branch and where the repo is, so if you create a new repo from the template this badge will be pointing to your workflow.
|
||||
|
||||
[](https://github.com/sirlilpanda/kicad-project-template/actions/workflows/main.yaml)
|
||||
|
||||
- `lastest_action_run_link`: </br>
|
||||
this is a link to the latest action run, as this action run has all the step summaries so you can have an easy link to show the output of the latest summary action
|
||||
|
||||
- `did_error`: </br>
|
||||
this allows the mustache templates to have a small amount of control flow allowing for different content to show if any of the workflow errors.
|
||||
|
||||
- `title`: </br>
|
||||
this is the project name that is defined in the project settings
|
||||
|
||||
- `multiple_projects`: </br>
|
||||
this is another flag to allow for control flow within the project, this defines whether there is multiple kicad project within the repo
|
||||
|
||||
- `projects`: </br>
|
||||
this is a list of the projects with serval names fields that are listed below. yes this list should remain in order if you want to do some strange stuff with indexing through it
|
||||
|
||||
- `projects.project_name`:
|
||||
this is the name of the project that is defined in project settings for the given project
|
||||
|
||||
- `projects.project_link`:</br>
|
||||
a link to the folder that the kicad project is in the repo for the given project
|
||||
|
||||
- `projects.passing_erc`: </br>
|
||||
a flag to say if the given project is passing erc for the given project
|
||||
|
||||
- `projects.passing_erc_emoji`: </br>
|
||||
an emoji for is the project is passing/failing erc this is currently defined in the [processing output files script](https://github.com/sirlilpanda/kicad-project-template/blob/main/.github/report_processing/process_output_files.py) currently passing is ✅ and failing is ❌.
|
||||

|
||||
|
||||
- `projects.erc_summary_link`: </br>
|
||||
a link to the summary created in the workflow for the given project
|
||||
|
||||
- `projects.passing_drc`: </br>
|
||||
a flag to say if the given project is passing drc for the given project
|
||||
|
||||
- `projects.passing_drc_emoji`: </br>
|
||||
an emoji for is the project is passing/failing drc this is currently defined in the [processing output files script](https://github.com/sirlilpanda/kicad-project-template/blob/main/.github/report_processing/process_output_files.py) currently passing is ✅ and failing is ❌.
|
||||

|
||||
|
||||
- `projects.drc_summary_link`: </br>
|
||||
a link to the summary created in the workflow for the given project
|
||||
|
||||
- `projects.gerber_link`: </br>
|
||||
a link to gerbers created by the given project in the github repo
|
||||
|
||||
- `projects.schematic_link`: </br>
|
||||
a link to schematic pdf created by the given project in the github repo
|
||||
|
||||
- `projects.bom_report_link`: </br>
|
||||
a link to bom report created by the given project in the github repo
|
||||
|
||||
- `projects.bom_csv_link`: </br>
|
||||
a link to bom csv created by the given project in the github repo
|
||||
|
||||
using these some cool readmes can be created like the [current one](https://github.com/sirlilpanda/kicad-project-template/blob/main/README.md) on the project. Where the table at the top not only shows the status of erc and drc but also the emojis link to the summary step repo. As can be seen below is the current readme template but this time with multiple failing projects in the repo.
|
||||

|
||||
as can be seen in it the table of erc and drc shows that parts of each of them are failing and the links to produced files from all the projects below within a table.`
|
||||
for the top table showing erc and drc checks using this mustache
|
||||
```mustache
|
||||
| project_name | DRC | ERC |
|
||||
| ------------ | --- | --- |
|
||||
{{#projects}}
|
||||
| {{project_name}} | [{{passing_erc_emoji}}]({{erc_summary_link}})| [{{passing_drc_emoji}}]({{drc_summary_link}}) |
|
||||
{{/projects}}
|
||||
```
|
||||
where this dynamically changing table which uses this mustache code
|
||||
```mustache
|
||||
{{^multiple_projects}}
|
||||
{{#projects}}
|
||||
- [gerbers]({{gerber_link}})
|
||||
- [bom]({{bom_report_link}})
|
||||
- [schematic pdf]({{schematic_link}})
|
||||
{{/projects}}
|
||||
{{/multiple_projects}}
|
||||
|
||||
{{#multiple_projects}}
|
||||
| project_name | schematic | bom | bom report | gerbers |
|
||||
| ------------ | --------- | --- | ---------- | ------- |
|
||||
{{#projects}}
|
||||
| [{{project_name}}]({{project_link}}) | [{{project_name}}_schematic.pdf]({{schematic_link}}) | [{{project_name}}_bill_of_materials.csv]({{bom_csv_link}}) | [{{project_name}}_bom_report.md]({{bom_report_link}}) | [{{project_name}}_grbr.zip]({{gerber_link}}) |
|
||||
{{/projects}}
|
||||
{{/multiple_projects}}
|
||||
```
|
||||
|
||||
the [processing output files script](https://github.com/sirlilpanda/kicad-project-template/blob/main/.github/report_processing/process_output_files.py) takes all the `*.json` files created by the previous steps and creates the hash for the readme. it firstly strips out the `readme_extras.json` so if you want to add any other information to the hash i suggest you either add it in here or you modify the script. But more importantly i finally found a spot where i could use an else clause with a for loop and i just wanted to show it off. This snippet of combines all the `*.json` files using the `project_name` as a comment value to join all the json files in to a singular dictionary so if you want to add something in to every project in the project list just creates a `*.json` file with the line `"project_name" : {{matrix.project_name}}` and it should automatically to the project objects in the project list.
|
||||
```python
|
||||
for report in reports_dicts:
|
||||
for project in readme_hash["projects"]:
|
||||
if project["project_name"] == report["project_name"]:
|
||||
for key in report.keys():
|
||||
project.setdefault(key, report[key])
|
||||
break
|
||||
else:
|
||||
readme_hash["projects"].append(report)
|
||||
```
|
||||
|
||||
dont forget this [template](https://github.com/sirlilpanda/kicad-project-template/blob/main/.github/report_templates/readme.mustache) can be changed for your use or more can be added within the [processing output files script](https://github.com/sirlilpanda/kicad-project-template/blob/main/.github/report_processing/process_output_files.py).
|
||||
|
||||
|
||||
and to top it off the steps of the job.
|
||||
|
||||
1. `checkout`
|
||||
cant run the update the readme if i dont have the repo
|
||||
|
||||
2. `read yaml file`
|
||||
need to check if the readme should be updated
|
||||
|
||||
3. `download ercs files`
|
||||
this downloads all the `{{matrix.project_name}}_erc.json` files for compiling
|
||||
|
||||
4. `download drcs files`
|
||||
this downloads all the `{{matrix.project_name}}_drc.json` files for compiling
|
||||
|
||||
5. `download productions files`
|
||||
this downloads all the `{{matrix.project_name}}_project.json` files for compiling
|
||||
|
||||
6. `Setting up Python`
|
||||
need something to run the script
|
||||
|
||||
7. `installing requirements`
|
||||
cant run the python script if you dont have the requirements
|
||||
|
||||
8. `create extra info for readme hash`
|
||||
this step creates all the extra info for the hash, like the badge, latest action run link and the title
|
||||
|
||||
9. `create new readme`
|
||||
creates the new readme with the python script
|
||||
|
||||
10. `upload data for readme updating`
|
||||
uploads the readme for the last job to commit everything back to the repo
|
||||
|
||||
## uploading production files
|
||||
|
||||
this last job takes all the production files created and uploads them to the repo, the only files that arnt uploaded are the `*.json` that are created to make the readme, as in this step the read me gets committed too. There really isnt much to it.
|
||||
|
||||
so the step to do this are:
|
||||
|
||||
1. `checkout`
|
||||
cant push to the repo if i dont have it
|
||||
|
||||
2. `read yaml file`
|
||||
this is needed to get the correct paths to the location
|
||||
|
||||
3. `download production files`
|
||||
this downloads all the production files made by the [production-files](#production-files) step.
|
||||
|
||||
4. `download readme`
|
||||
downloads the readme from the [updating the readme](#updating-the-readme) step
|
||||
|
||||
5. `check downloads`
|
||||
just lists the current dir to check everything has been downloaded and where they are
|
||||
|
||||
6. `Moving files to correct location`
|
||||
this moves the production files from their downloaded location to their new location
|
||||
|
||||
7. `commit production files`
|
||||
lastly this commits all the production files to the repo
|
||||
|
||||
# improvements and future work
|
||||
|
||||
currently there are 4 cool ideas i am looking in to for future improvements to this template.
|
||||
|
||||
1. bom cost measurements </br>
|
||||
this idea which was perviously talked about in the [Production files](#production-files) section, where i create some code to make api calls to some electronic parts supplier but this is annoying and will take a while to compute, as the [mouser api](https://nz.mouser.com/api-search/) only allows for 30 parts to be searched per minute, which will slow down the workflow. ive heard the digikey api, requires you updated your token monthly which people wont do and the workflow will break. Lastly [octopart](https://octopart.com/business/api/v4/api-transition) this thing is fucking dumb, $100 per month just for 10k searches what a scam. 
|
||||
|
||||
2. 3d view of the pcb in the readme </br>
|
||||
this idea would create a 3d view of the main pcb to use in the readme, this would be done through using something like [this headless python opengl context](https://github.com/szabolcsdombi/headless-moderngl-experiment) and exporting the [PCB as a vrml](https://docs.kicad.org/8.0/en/cli/cli.html#pcb_vrml_export). This would allow for a fully 3d colour render of the pcb
|
||||
|
||||
3. more flexible project layout </br>
|
||||
this idea would allow the user to defined the output dir of all production files and where certain files are stored/called. so if you dont like the [layout structure](#project-structure) of the project then you could change it. or many if you dont like how i called the folders `Hardware/` instead of `hardware/` or even `{{project name}}_PROJECT` could then be changed to `hardware_{{project name}}`.
|
||||
|
||||
4. testing </br>
|
||||
currently when i add a new feature to the repo i have a set of integration tests i have to complete to make sure everything still works, but this is very time consuming so some automatic test pipe line that would check when the template has been pushed to and then testing all the cases like, multiple projects, failing project, readme output and summary step outputs. would be very nice to have.
|
||||
@@ -1,218 +0,0 @@
|
||||
---
|
||||
title: 'Markdown Style Guide'
|
||||
description: 'Here is a sample of some basic Markdown syntax that can be used when writing Markdown content in Astro.'
|
||||
pubDate: 'Jun 19 2024'
|
||||
heroImage: '../../assets/blog-placeholder-1.jpg'
|
||||
---
|
||||
|
||||
<!-- # TOC -->
|
||||
```math
|
||||
f(x) = ym + c
|
||||
```
|
||||
Here is a sample of some basic Markdown syntax that can be used when writing Markdown content in Astro.
|
||||
|
||||
## Headings
|
||||
|
||||
The following HTML `<h1>`—`<h6>` elements represent six levels of section headings. `<h1>` is the highest section level while `<h6>` is the lowest.
|
||||
|
||||
# H1
|
||||
|
||||
## H2
|
||||
|
||||
### H3
|
||||
|
||||
#### H4
|
||||
|
||||
##### H5
|
||||
|
||||
###### H6
|
||||
|
||||
## Paragraph
|
||||
|
||||
Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.
|
||||
|
||||
Itatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.
|
||||
|
||||
## Images
|
||||
|
||||
### Syntax
|
||||
|
||||
```markdown
|
||||

|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||

|
||||
|
||||
## Blockquotes
|
||||
|
||||
The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a `footer` or `cite` element, and optionally with in-line changes such as annotations and abbreviations.
|
||||
|
||||
### Blockquote without attribution
|
||||
|
||||
#### Syntax
|
||||
|
||||
```markdown
|
||||
> Tiam, ad mint andaepu dandae nostion secatur sequo quae.
|
||||
> **Note** that you can use _Markdown syntax_ within a blockquote.
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
> Tiam, ad mint andaepu dandae nostion secatur sequo quae.
|
||||
> **Note** that you can use _Markdown syntax_ within a blockquote.
|
||||
|
||||
### Blockquote with attribution
|
||||
|
||||
#### Syntax
|
||||
|
||||
```markdown
|
||||
> Don't communicate by sharing memory, share memory by communicating.<br>
|
||||
> — <cite>Rob Pike[^1]</cite>
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
> Don't communicate by sharing memory, share memory by communicating.<br>
|
||||
> — <cite>Rob Pike[^1]</cite>
|
||||
|
||||
[^1]: The above quote is excerpted from Rob Pike's [talk](https://www.youtube.com/watch?v=PAAkCSZUG1c) during Gopherfest, November 18, 2015.
|
||||
|
||||
## Tables
|
||||
|
||||
### Syntax
|
||||
|
||||
```markdown
|
||||
| Italics | Bold | Code |
|
||||
| --------- | -------- | ------ |
|
||||
| _italics_ | **bold** | `code` |
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
| Italics | Bold | Code |
|
||||
| --------- | -------- | ------ |
|
||||
| _italics_ | **bold** | `code` |
|
||||
|
||||
## Code Blocks
|
||||
|
||||
### Syntax
|
||||
|
||||
we can use 3 backticks ``` in new line and write snippet and close with 3 backticks on new line and to highlight language specific syntax, write one word of language name after first 3 backticks, for eg. html, javascript, css, markdown, typescript, txt, bash
|
||||
|
||||
````markdown
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Example HTML5 Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
````
|
||||
|
||||
### Output
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Example HTML5 Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test</p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## List Types
|
||||
|
||||
### Ordered List
|
||||
|
||||
#### Syntax
|
||||
|
||||
```markdown
|
||||
1. First item
|
||||
2. Second item
|
||||
3. Third item
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
1. First item
|
||||
2. Second item
|
||||
3. Third item
|
||||
|
||||
### Unordered List
|
||||
|
||||
#### Syntax
|
||||
|
||||
```markdown
|
||||
- List item
|
||||
- Another item
|
||||
- And another item
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
- List item
|
||||
- Another item
|
||||
- And another item
|
||||
|
||||
### Nested list
|
||||
|
||||
#### Syntax
|
||||
|
||||
```markdown
|
||||
- Fruit
|
||||
- Apple
|
||||
- Orange
|
||||
- Banana
|
||||
- Dairy
|
||||
- Milk
|
||||
- Cheese
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
- Fruit
|
||||
- Apple
|
||||
- Orange
|
||||
- Banana
|
||||
- Dairy
|
||||
- Milk
|
||||
- Cheese
|
||||
|
||||
## Other Elements — abbr, sub, sup, kbd, mark
|
||||
|
||||
### Syntax
|
||||
|
||||
```markdown
|
||||
<abbr title="Graphics Interchange Format">GIF</abbr> is a bitmap image format.
|
||||
|
||||
H<sub>2</sub>O
|
||||
|
||||
X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
|
||||
|
||||
Press <kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>Delete</kbd> to end the session.
|
||||
|
||||
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
<abbr title="Graphics Interchange Format">GIF</abbr> is a bitmap image format.
|
||||
|
||||
H<sub>2</sub>O
|
||||
|
||||
X<sup>n</sup> + Y<sup>n</sup> = Z<sup>n</sup>
|
||||
|
||||
Press <kbd>CTRL</kbd> + <kbd>ALT</kbd> + <kbd>Delete</kbd> to end the session.
|
||||
|
||||
Most <mark>salamanders</mark> are nocturnal, and hunt for insects, worms, and other small creatures.
|
||||
47
src/content/blog/micro-soldering.md
Normal file
47
src/content/blog/micro-soldering.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: 'micro soldering'
|
||||
description: 'A challange PCB i solder, filled with what was the worlds smallest capacitors'
|
||||
pubDate: 'Feb 10 2023'
|
||||
heroImage: '../../assets/article_hero_images/usmd.png'
|
||||
---
|
||||
|
||||
my journy into micro soldering started with watching a video on the [AttinyChallengeMisery](https://github.com/aspro648/KiCad/tree/master/projects/Attiny/AttinyChallengeMisery) where you must solder progressively smaller components form 0805(imperial) down to 01005. however i didnt think of trying my hand at anything like that till i saw a [video from marco reps](https://www.youtube.com/watch?v=2vpTYaDGmCs&t=1451s) soldering another soldering challenge the (uSMD)[https://github.com/arthurbenemann/usmd?tab=readme-ov-file]. this chanllenge envoles soldering:
|
||||
|
||||
- x10 10000PF 008004 capacitors
|
||||
|
||||
- x1 2SMD LED
|
||||
|
||||
- x1 1k 01005 resistor
|
||||
|
||||
- x1 10M 01005 resistor
|
||||
|
||||
- x1 3MHZ 8-USMD 555 timer (BGA)
|
||||
|
||||
|
||||
needless to say that this is not the easiest thing to do. the 008004 capacitors are 0.25×0.125mm. here is an image of one next to a human hair:
|
||||
|
||||

|
||||
|
||||
so as you can see (or not in this case) they are incredibly small, and 10 of these have to be soldered. luckily there are only 2 01005 resistor that need soldering, but even those these are larger then the caps they are still tiny as fuck. the LED that needs soldering just like the 555 timer has its pads underneath because they want us to feel pain in this project. lastly the 555 time is in a bare die package which is admittedly quite cool to look at, although there are 8 BGA balls on it so impossible to solder with an iron.
|
||||

|
||||
picture of the 555 timer very pretty.
|
||||
|
||||
## the setup
|
||||
as you can tell since i have taken on this challenge i am definitely not in the right state of mind.
|
||||
below is an image of the size comparison of the tools i am using. in the top left is the solder roll just underneath that is 1 fork of my tweezers and lastly at the bottom is the tip of my soldering iron. And unlike marco reps in his video where he used brand new [JBC soldering equipment](https://www.jbctools.com/) i dont have \$10k to spend on new equipment, so i bought a \$300 dollar microscope form jaycar a brand new set of tip for my [\$40 indirect heat soldering iron from aliexpress](https://www.aliexpress.com/item/1005006427194224.html?spm=a2g0o.productlist.main.5.367f22a6ioxJqh&algo_pvid=f73c0b15-032a-4af1-882b-b4713f0efebf&algo_exp_id=f73c0b15-032a-4af1-882b-b4713f0efebf-2&pdp_npi=4%40dis%21NZD%21131.91%2160.05%21%21%2179.18%2136.05%21%40210307c017191470943688199e6f2a%2112000037128419911%21sea%21NZ%212538725004%21&curPageLogUid=LseGb0C6mmki&utparam-url=scene%3Asearch%7Cquery_from%3A) and a $10 set of tweezers form aliexpress. so as you can see only the highest quality of tools.
|
||||

|
||||
|
||||
below is the setup i was working at. it is the roll of solder i was using masking taped the lid of my PCB cleaner can which was then kapton taped to a another pcb and finally the uSMD PCB was bluetacked to the PCB. this set up was done as the PCB had to be very close to the microscope to be able to see anything as well also needing to be that high up in order to not be sitting like a shrimp with my back fully curled up.
|
||||

|
||||
|
||||
# the process
|
||||
i first started this project by soldering on the 555 timer, and as i dont have an oven or a tip large enough to make a little hot plate, i instead deicide on hot air. so to solder it on i applied a liberal amount of flux to the PCB placed the 555 timer on top, then not holding the 555 timer but pushing down on it with my tweezers and blasting it with hot air. i surprisingly only lost 1 555 timer this way, however this was the easy part. The hard part as expected was the 008004, for these i tinned one pad then did the ungodly sin of painting solder on the other side. i did this painting technique as my soldering iron was not small enough to get right to the pad so my rational was. if i just coating that side in in a molten blob of solder some solder would stick to the pad and the capacitor. for the most part this technique worked although there were some losses, i.e. > 50%. but that was mainly because i couldnt fucking see them when i took them out of the package.
|
||||
|
||||
here is the final image of the soldering, that big bridge at the top was from me attempting to coat the board in uv resin to protect it. but after trying it on that capacitor i didnt like how it turned out so i slowly removed it and with removing it i took some of the trace and capacitor with it. so i had to scrape back some of the solder mask and attempt to make a long enough bridge of solder from that to the capacitor.
|
||||

|
||||
|
||||
and here is the video of it working, with appropriate language from managing to solder everything correctly.
|
||||

|
||||
|
||||
# conclusion
|
||||
over all i think this challenge is not for everyone but it was a really good test of my steadiness as well a great way to get bragging rights over your friends.
|
||||
24
src/content/blog/mini-essays.md
Normal file
24
src/content/blog/mini-essays.md
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
title: 'mini essays and why im going to start writing them'
|
||||
description: 'explaining my opioions around mini essays'
|
||||
pubDate: 'Feb 2 2026'
|
||||
heroImage: '../../assets/article_hero_images/person-writing-on-paper.jpeg'
|
||||
---
|
||||
|
||||
Just as the title says I'm going to start writing mini essays, but what are mini essays why am I
|
||||
writing them (and maybe why you should be writing them too). Mini essays or sometimes called
|
||||
microthemes (no don't ask me why I can't get access to the book article that mentions them) are small
|
||||
essays being around 100-500 words, with this number chosen to allow the whole essay to be typed on a
|
||||
5"x8" notecard. However, I would just suggest that you use the number of words that you need.
|
||||
|
||||
Now that I have explained the basic length of a mini essay, you may be asking yourself "cool it's a small piece of writing, but why should I be writing these".
|
||||
The original idea behind mini essays was to create small piece of work that can be easily marked within large classes. Now you are asking yourself "why do I care if someone can easily mark my work, and I'm not even in a large class". Don't worry that's only one application of mini essays they are primarily design to promote your own understanding in a given subject. So say you are learning about how to bake bread, instead of just taking notes on about it, you take those notes and write a small work trying to teach someone else how to bake bread. This type of writing where you are teaching someone else is going to help your own understanding of the subject itself due to requiring you to recall what you have previously learnt.
|
||||
|
||||
So why am I writing mini essays, well after 6 months of starting my PhD I had to write a proposal. However, I had gone roughly ~8 months without writing any long form content (i.e. reports). This time span caused my ability to write to atrophy. So in writing these mini essays I hope to keep my skills in writing honed. Although this is only the primary reason I will be writing mini essays. The second and more fun reason is I tend to focus learning on a wide and I do mean a *wide* variety of different subject and skill, and while researching each of these I tend to forget things never write them down then lose that knowledge. Therefor my plan is to write these mini essays in order to attempt to retain this information, at least for a slightly longer span of time. Additionally, I plan to write and host each of my mini essay on this very website, and you may ask "why would someone want to read the incoherent ramblings of a mad man". Well with the things I tend to look into my bets are someone is bound to read and hopefully find that one piece of information they are missing for their understanding. Hosting the mini essays on my website also allows me to easily access them rather than have to open up a markdown file somewhere that I will inevitably lose. So that's why I will be writing mini essays, so why don't you.
|
||||
|
||||
Some additional resources you may want to read and I definitely reference within this mini essay:
|
||||
- Mini Essays: The Ultimate Learning Tool : https://www.youtube.com/watch?v=N4YjXJVzoZY
|
||||
- Microtheme strategies for developing cognitive skills : https://onlinelibrary.wiley.com/doi/10.1002/tl.37219821205
|
||||
- Mini Essays : https://writingcenter.tamu.edu/faculty-advisors/resources-for-teaching/instruction/mini-essays
|
||||
- Learning by teaching others is extremely effective : https://www.bps.org.uk/research-digest/learning-teaching-others-extremely-effective
|
||||
|
||||
128
src/content/blog/ray-tracer.md
Normal file
128
src/content/blog/ray-tracer.md
Normal file
@@ -0,0 +1,128 @@
|
||||
---
|
||||
title: 'ray tracer'
|
||||
description: 'a report i did for a computer graphics coruse'
|
||||
pubDate: 'Jun 04 2023'
|
||||
heroImage: '../../assets/article_hero_images/ray_tracer.png'
|
||||
---
|
||||
|
||||
# Introduction
|
||||
The ray tracer works by casting rays from a point to a through a point pixel gird, corresponding to the colour of the pixel that the ray traced. This illumination model allows us to only consider the light reaching the camera. the recursive ray tracing that is implemented here allows for serval key features, the ability to render, transparent, refractive, and reflective properties. This is in conjunction with the ability to render, planes, spheres, cones, truncated cones, double cone, and cylinders with caps.
|
||||
The simulation takes roughly 1 minute and 10 seconds to render a 1000x1000 pixel grid with a max depth of 6, running on a ryzen 9 5900x.
|
||||
|
||||
# Transparent & refractive object shadows
|
||||
Both transparent and refractive object cast lighter shadows based on their coefficients and colors. This was done by checking to see if the closest object intersecting the point to the light source, was either transparent or refractive and not the object itself. If the object the shadow was being cast on to be the object that it was the closest intersection, as in if the objects were the same object, the transparent or refractive would end up with a shadow on it like a regular object, this would no look correct as a clear object should not have a shadow. Once this check was done it would deice how to color the point based on the color of the transparent or refractive object.
|
||||
|
||||
- If the color of the object was black, then it would multiply the color of the point with either the transparentCoeff or refractionCoeff.
|
||||
|
||||
- If the color was within the gray tones or white, as in if all RGB channels were equal, then the gray tone would be multiplied by the transparentCoeff or refractionCoeff and subtracted from the color at the point. The reason for this subtraction is that this would then move the value of the color at that point closer to zero, there for the color would become darker, showing that it is in shadow.
|
||||
|
||||
- Finally, if the color was neither black nor gray/white, the color of the transparent or refractive is be multiplied by the transparentCoeff or refractionCoeff. Then each of the color channel of the object, has the max value of the RGB channels subtracted from it. This causes the value at that point that is in shadow to have a max value of the color that is going through the transparent or refractive object.
|
||||
|
||||
```cpp
|
||||
|
||||
glm::vec3 c = sceneObjects[shadowRay.index]->getColor();
|
||||
if (c == glm::vec3(0)){
|
||||
color *= sceneObjects[shadowRay.index]->getRefractionCoeff();
|
||||
}else if (c.x == c.y && c.y == c.z){
|
||||
printf("c : %f\n", 1-sceneObjects[shadowRay.index]->getRefractionCoeff());
|
||||
color -= sceneObjects[shadowRay.index]->getRefractionCoeff()*c.x;
|
||||
} else {
|
||||
c *= sceneObjects[shadowRay.index]->getRefractionCoeff();
|
||||
glm::vec3 c_max = glm::vec3(std::max({c.x, c.y, c.z}));
|
||||
color -= c_max - c;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
This technique creates lighter shadows for transparent or refractive object while retaining the color that would be passing through them.
|
||||
|
||||
# Texture mapping
|
||||
There are two shape that are texture mapped, the sphere and cone. The sphere uses a simple algorithm of uv wrapping where points of the sphere are mapped to a 2d texture. this unwrapping is done by converting the hit location in from rectangular coordinates to spherical coordinates where:
|
||||
$$
|
||||
u=\frac{tan^{-1}\frac{n_x}{n_z}}{\left(2\ast\pi\right)}\ +\ 0.5
|
||||
$$
|
||||
$$
|
||||
v=\frac{n_y}{2}+0.5
|
||||
$$
|
||||
Where n is the normalized vector of the hit subtracted from the center. Both are added by 0.5 to properly center it on the 2d image. As $tan^{-1}x/y$ is returns numbers in the domain of $ \left(-\pi,\ \pi\ \right) $. The texture mapping for the cylinder maps the curved portion of the cylinder to the 2d image and maps the top of the cylinder to the top of the 2d image.
|
||||
$$
|
||||
u=\frac{tan^{-1}\frac{n_x}{n_z}+\pi}{2\ast\pi}
|
||||
$$
|
||||
$$
|
||||
v=\frac{n_y}{h}
|
||||
$$
|
||||
Where h is the height of the cylinder. When mapping on to the top flat region of the cylinder the $u = 1$ and $v = n_x$. This maps the top of the cylinder to just the very top of the 2d image. This allows for an almost seamless texture mapping.
|
||||
|
||||
# Cylinder
|
||||
## Intersections
|
||||
The cylinders were created by find both t values that satisfy the equation for a vector intersecting a cylinder:
|
||||
$$
|
||||
t^2\left(d_x^2+d_z^2\right)+2t\left(d_x\left(x_0-x_c\right)+d_z\left(z_0-z_c\right)\right) \\ +\left(\left(x_0-x_c\right)^2+\left(z_0-z_c\right)^2-R^2\right)=0
|
||||
$$
|
||||
If the value of t was in the imaginary plane, then the vector did not intersect the cylinder. although this is the equation for an uncapped cylinder. To find the cap of the cylinder the values of t were checked, if $t1$ was above the height of the cylinder and $t2$ was below the height of the cylinder. then the vector was intersecting the top of the cylinder, with the value of t at that point being $t2 – t1$.
|
||||
## Normal
|
||||
The normal of the cylinder is computed by the point where the normal is subtracted by the center. This is vector is then normalized by dividing the radius of the cylinder:
|
||||
$$
|
||||
n=\left(\left(x-x_c\right)/R,0,\left(z-z_c\right)/R\right)
|
||||
$$
|
||||
The y component is set to zero unless the vector is intersecting the top of the cylinder. This is because the normal does not change with differing values of y unless the normal is at the top of the cylinder. for calculating the normal at the top of the cylinder, the normal will remain constant no matter what values x, y, or z are therefor it returns $(0, 1, 0)$
|
||||
|
||||
# Cones
|
||||
## Intersection
|
||||
The cones intersection is calculated in a similar fashion to that of the cylinder, expect for the cone will instead check its cut off points at its height limit value. This is done so one equation can be used for drawing truncated cones, normal cones and double cones. The intersection equation for the cone is:
|
||||
$$
|
||||
t^2\left(d_x^2+d_z^2-r_2d_y^2\right)+ \\ 2t\left(dx\left(x_o-x_c\right)+d_z\left(zo-zc\right)+ r_2d_y\left(h-y_o+y_c\right)\right)\ \\ +\ (\left(x_0-x_c\right)^2+\left(z_0-z_c\right)^2-r_2\left(h-y_o+y_c\right)^2
|
||||
$$
|
||||
Where r_2 equal to the radius over the height squared. With the same methods of finding valid values of t for where it intersects as well as if there is cap on the cone for if it is truncated or a double cone.
|
||||
## Normal
|
||||
The normal for a point on a cone, is calculated by finding the length of the x and z components hit point on the cone and returning a normalized vector of:
|
||||
$$
|
||||
\left(hit_x-c_x,\left|\left|\left(hit_x-c_x,hit_z-c_z\right)\right|\right|\ast\left(r/h\right),hit_z-c_z\right)
|
||||
$$
|
||||
And if the normal was being calculated for a cap the same method was used as the one outlined in the cylinder.
|
||||
|
||||
# Sphere example:
|
||||

|
||||
Figure 1: Sphere raytracing example
|
||||
In this figure 4 spheres can be seen, 1 that has a texture map image of earth on it, a transparent purple one over to the left, a refractive in the center and finally a reflective sphere on the right. All these spheres also have specular reflections, as can be seen by the shiny spot on them.
|
||||
As can be seen on the transparent purple sphere, the shadow cast by it is not only lighter but also, the same color as the sphere itself, using the method outlined in Transparent & refractive object shadows.
|
||||
The refractive sphere in the center displays the image of the room but upside down. This is because the sphere has a refractive index of a $η = 1.5$. although it is hard to see the sphere casting a light shadow onto the ground.
|
||||
In the reflective sphere on the right-hand side the refractive sphere can be seen through it refracting off the red from the left-hand wall.
|
||||
Finally the larger textured sphere in the back center, is textured using the method in texturing, as well due to how it is implemented the textured sphere could also be transparent although this leads to incorrect lighting in the shadows.
|
||||
|
||||
# Cone example:
|
||||

|
||||
Figure 2: cone Example
|
||||
As can be seen in figure 2, there are 3 cones, one truncated, one normal and reflective and one double cone. The top of the truncated cone appears to be white due to the light being from up high and the cone having specular reflections so the top would appear to be quite shiny. The center cone is reflective, and the double cone can be seen in its reflection to the left, as well as both walls. The double cone shadow can be seen clearly behind it.
|
||||
|
||||
# Cylinder example:
|
||||

|
||||
Figure 3: cylinder example
|
||||
As can be seen in Figure 3 there are 3 cylinders, 1 white one, a reflective one in the center and one texture mapped one to the right. The within the center cylinder both white cone and texture mapped cylinder can be seen. The white cylinder as the same white top as the green cone as seen before due to the same reasons as well as the reflective cylinder reflecting all the light from the source above it.
|
||||
the texture mapped cone can be seen with a white top corresponding the top of the texture.
|
||||
|
||||
# Experimental roughness/light bouncing
|
||||

|
||||
Figure 4: light bouncing experiment
|
||||
The image as seen in figure 4 shows a rough pass, of having light scatter when hitting an object and sampling other objects with random vectors to simulate a more accurate version of reality. This can be seen more at the intersection of the left red wall and the striped ground, where the left wall is slightly colored by the light bouncing off the floor. Although not as noticeable the sphere can also be seen with a red tint on the left and a blueish tint on the right. This was very computationally expensive.
|
||||
|
||||
# Mirror bouncing
|
||||

|
||||
Figure 5: bouncing through mirrors.
|
||||
|
||||
Figure 5 depicts a room with 2 mirrors at either end with a model of the earth within it. The model earth can be seen to be repeated through all the mirrors within this room. A slightly more impressive example can be seen in appendix A
|
||||
|
||||
# Appendix
|
||||
These are a few of the cool test renders I had when developed the ray tracer.
|
||||
|
||||
## A: album cover
|
||||

|
||||
|
||||
## B: album cover
|
||||

|
||||
|
||||
## C: album cover
|
||||

|
||||
|
||||
## D: canned earth
|
||||

|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
title: 'Second post'
|
||||
description: 'Lorem ipsum dolor sit amet'
|
||||
pubDate: 'Jul 15 2022'
|
||||
heroImage: '../../assets/blog-placeholder-4.jpg'
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vitae ultricies leo integer malesuada nunc vel risus commodo viverra. Adipiscing enim eu turpis egestas pretium. Euismod elementum nisi quis eleifend quam adipiscing. In hac habitasse platea dictumst vestibulum. Sagittis purus sit amet volutpat. Netus et malesuada fames ac turpis egestas. Eget magna fermentum iaculis eu non diam phasellus vestibulum lorem. Varius sit amet mattis vulputate enim. Habitasse platea dictumst quisque sagittis. Integer quis auctor elit sed vulputate mi. Dictumst quisque sagittis purus sit amet.
|
||||
|
||||
Morbi tristique senectus et netus. Id semper risus in hendrerit gravida rutrum quisque non tellus. Habitasse platea dictumst quisque sagittis purus sit amet. Tellus molestie nunc non blandit massa. Cursus vitae congue mauris rhoncus. Accumsan tortor posuere ac ut. Fringilla urna porttitor rhoncus dolor. Elit ullamcorper dignissim cras tincidunt lobortis. In cursus turpis massa tincidunt dui ut ornare lectus. Integer feugiat scelerisque varius morbi enim nunc. Bibendum neque egestas congue quisque egestas diam. Cras ornare arcu dui vivamus arcu felis bibendum. Dignissim suspendisse in est ante in nibh mauris. Sed tempus urna et pharetra pharetra massa massa ultricies mi.
|
||||
|
||||
Mollis nunc sed id semper risus in. Convallis a cras semper auctor neque. Diam sit amet nisl suscipit. Lacus viverra vitae congue eu consequat ac felis donec. Egestas integer eget aliquet nibh praesent tristique magna sit amet. Eget magna fermentum iaculis eu non diam. In vitae turpis massa sed elementum. Tristique et egestas quis ipsum suspendisse ultrices. Eget lorem dolor sed viverra ipsum. Vel turpis nunc eget lorem dolor sed viverra. Posuere ac ut consequat semper viverra nam. Laoreet suspendisse interdum consectetur libero id faucibus. Diam phasellus vestibulum lorem sed risus ultricies tristique. Rhoncus dolor purus non enim praesent elementum facilisis. Ultrices tincidunt arcu non sodales neque. Tempus egestas sed sed risus pretium quam vulputate. Viverra suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Fringilla urna porttitor rhoncus dolor purus non. Amet dictum sit amet justo donec enim.
|
||||
|
||||
Mattis ullamcorper velit sed ullamcorper morbi tincidunt. Tortor posuere ac ut consequat semper viverra. Tellus mauris a diam maecenas sed enim ut sem viverra. Venenatis urna cursus eget nunc scelerisque viverra mauris in. Arcu ac tortor dignissim convallis aenean et tortor at. Curabitur gravida arcu ac tortor dignissim convallis aenean et tortor. Egestas tellus rutrum tellus pellentesque eu. Fusce ut placerat orci nulla pellentesque dignissim enim sit amet. Ut enim blandit volutpat maecenas volutpat blandit aliquam etiam. Id donec ultrices tincidunt arcu. Id cursus metus aliquam eleifend mi.
|
||||
|
||||
Tempus quam pellentesque nec nam aliquam sem. Risus at ultrices mi tempus imperdiet. Id porta nibh venenatis cras sed felis eget velit. Ipsum a arcu cursus vitae. Facilisis magna etiam tempor orci eu lobortis elementum. Tincidunt dui ut ornare lectus sit. Quisque non tellus orci ac. Blandit libero volutpat sed cras. Nec tincidunt praesent semper feugiat nibh sed pulvinar proin gravida. Egestas integer eget aliquet nibh praesent tristique magna.
|
||||
23
src/content/blog/the-unethical-studying-method.md
Normal file
23
src/content/blog/the-unethical-studying-method.md
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
title: 'unethical studying method'
|
||||
description: |
|
||||
A theoretical method of studying that will never be used since it will never meet ethical standards
|
||||
heroImage: '../../assets/article_hero_images/studying.png'
|
||||
pubDate: 'Mar 25 2026'
|
||||
---
|
||||
|
||||
The unethical study method is a method of study, that being if someone was to run an experiment on it, it would never get ethical approval.
|
||||
This method itself has been the subject of many of conversation epically around test and exam time.
|
||||
This method is simple and requires a willing individual or yourself if you have some modicum of self-control and nicotine patches.
|
||||
The idea is around increasing the amount of dopamine that you get from getting a good score on a test.
|
||||
Normally if you have done well on a test or assignment you feel good, so this method is base on increasing that dopamine that you feel by using a nicotine patches when you do well on an assignment or test.
|
||||
This increase dopamine should hopefully get the user to get addicted to the high scores of the tests themselves and not the nicotine.
|
||||
Therefor an addiction to high test scores should in turn get the users themselves to study hard or at least enough to score well on the test.
|
||||
And now you can see the ethical problem with this, there is no ethical way to get someone addicted to something, and therefor no way to test it within a controlled environment, and therefor no way to test this fun though experiment.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
title: 'Third post'
|
||||
description: 'Lorem ipsum dolor sit amet'
|
||||
pubDate: 'Jul 22 2022'
|
||||
heroImage: '../../assets/blog-placeholder-2.jpg'
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vitae ultricies leo integer malesuada nunc vel risus commodo viverra. Adipiscing enim eu turpis egestas pretium. Euismod elementum nisi quis eleifend quam adipiscing. In hac habitasse platea dictumst vestibulum. Sagittis purus sit amet volutpat. Netus et malesuada fames ac turpis egestas. Eget magna fermentum iaculis eu non diam phasellus vestibulum lorem. Varius sit amet mattis vulputate enim. Habitasse platea dictumst quisque sagittis. Integer quis auctor elit sed vulputate mi. Dictumst quisque sagittis purus sit amet.
|
||||
|
||||
Morbi tristique senectus et netus. Id semper risus in hendrerit gravida rutrum quisque non tellus. Habitasse platea dictumst quisque sagittis purus sit amet. Tellus molestie nunc non blandit massa. Cursus vitae congue mauris rhoncus. Accumsan tortor posuere ac ut. Fringilla urna porttitor rhoncus dolor. Elit ullamcorper dignissim cras tincidunt lobortis. In cursus turpis massa tincidunt dui ut ornare lectus. Integer feugiat scelerisque varius morbi enim nunc. Bibendum neque egestas congue quisque egestas diam. Cras ornare arcu dui vivamus arcu felis bibendum. Dignissim suspendisse in est ante in nibh mauris. Sed tempus urna et pharetra pharetra massa massa ultricies mi.
|
||||
|
||||
Mollis nunc sed id semper risus in. Convallis a cras semper auctor neque. Diam sit amet nisl suscipit. Lacus viverra vitae congue eu consequat ac felis donec. Egestas integer eget aliquet nibh praesent tristique magna sit amet. Eget magna fermentum iaculis eu non diam. In vitae turpis massa sed elementum. Tristique et egestas quis ipsum suspendisse ultrices. Eget lorem dolor sed viverra ipsum. Vel turpis nunc eget lorem dolor sed viverra. Posuere ac ut consequat semper viverra nam. Laoreet suspendisse interdum consectetur libero id faucibus. Diam phasellus vestibulum lorem sed risus ultricies tristique. Rhoncus dolor purus non enim praesent elementum facilisis. Ultrices tincidunt arcu non sodales neque. Tempus egestas sed sed risus pretium quam vulputate. Viverra suspendisse potenti nullam ac tortor vitae purus faucibus ornare. Fringilla urna porttitor rhoncus dolor purus non. Amet dictum sit amet justo donec enim.
|
||||
|
||||
Mattis ullamcorper velit sed ullamcorper morbi tincidunt. Tortor posuere ac ut consequat semper viverra. Tellus mauris a diam maecenas sed enim ut sem viverra. Venenatis urna cursus eget nunc scelerisque viverra mauris in. Arcu ac tortor dignissim convallis aenean et tortor at. Curabitur gravida arcu ac tortor dignissim convallis aenean et tortor. Egestas tellus rutrum tellus pellentesque eu. Fusce ut placerat orci nulla pellentesque dignissim enim sit amet. Ut enim blandit volutpat maecenas volutpat blandit aliquam etiam. Id donec ultrices tincidunt arcu. Id cursus metus aliquam eleifend mi.
|
||||
|
||||
Tempus quam pellentesque nec nam aliquam sem. Risus at ultrices mi tempus imperdiet. Id porta nibh venenatis cras sed felis eget velit. Ipsum a arcu cursus vitae. Facilisis magna etiam tempor orci eu lobortis elementum. Tincidunt dui ut ornare lectus sit. Quisque non tellus orci ac. Blandit libero volutpat sed cras. Nec tincidunt praesent semper feugiat nibh sed pulvinar proin gravida. Egestas integer eget aliquet nibh praesent tristique magna.
|
||||
73
src/content/blog/tomography.md
Normal file
73
src/content/blog/tomography.md
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
title: 'tomography Reconstruction '
|
||||
description: 'a report i did for a advanced signal processing coruse'
|
||||
pubDate: 'Feb 05 2024'
|
||||
heroImage: '../../assets/article_hero_images/tomography.png'
|
||||
---
|
||||
# Introduction
|
||||
The process of image reconstruction is widely used within the medical fields for reconstructing data from a CT scan to show what the inside of a person looks like. This ability to see inside a person allows for doctors to identify problems within the body and treat them appropriately. Another field image re construction is also used in is nondestructive imaging of parts such as on airplanes. This type of imaging allows for checking on the internals of certain structural areas of the plane that would otherwise be expensive as well as damage structural integrity if done. Within this report the basic algorithm for filtered back projecting and comparing this to non-filtered back projections. This type of filter allows for a clearer image after back projecting, and it is the reason why it is filtered. To compare filtered and non-filtered reconstructions a standard reference for computed tomography will be used, the shepp-logan phantom [10].
|
||||
|
||||
# Applications
|
||||
The main application of image reconstruction is when an internal view of an object or person is needed but it is either too costly or dangerous to do so. This type of nondestructive and noninvasive view of is useful in several fields. One of the main fields for this is medical imaging. The main use for this type of imaging, is in C.T scanners and MRI machines [11]. These machines allow for noninvasive views in to the body rather than the more invasive biopsy[11].
|
||||
Another field where tomography is used is within ocean observation, measuring temperatures and currents. This form of tomography use acoustic waves instead of the higher power x-rays as used in medicine [12]. This form of tomography uses the changing speed of sound within the ocean to not only determine ocean temperatures but also depth.
|
||||
|
||||
# Background
|
||||
Image reconstruct works because of the “Fourier projection theorem” or Projection-slice theorem” [13]. This theorem states that the integral of a shape projection along some axis is a slice of the 2d Fourier transform of that shape projected along the same axis. A graphical view of this theorem can be seen in Figure 1. This method is called back projection. Plotting all these back projected angles creates a sinogram and example of which can be seen in Figure 2. This figure shows sinogram of a basic test image that can be found in Appendix B: basic image This theorem also states that an infinite number of projects are taken at different angles and the image can be perfectly reconstructed.
|
||||
|
||||

|
||||
Figure 1: example of Fourier projection theorem source: Public Domain, https://commons.wikimedia.org/w/index.php?curid=646788
|
||||
|
||||
However, as an infinite number of projections a very large number of angles must be computed to reconstruct the image. Another problem occurs with this projection, as the rotation happens around the origin in Fourier space points near the center in Fourier space have more samples where points further away from the center tend to be more sparsely populated. This is due to the polar sampling in Fourier space. Therefor interpolating between these values is needed to create a clearer image. This is done through a filtering step that uses a ramp filter in the Fourier domain to interpolate between these values.
|
||||
|
||||

|
||||
Figure 2: sinogram of basic image
|
||||
|
||||
As can be seen in this Figure 2: sinogram of basic image the sinogram is mirror at the 180° mark, however it is mirrored both horizontally and vertically. using this knowledge this means that when taking the back projections of an object, only must take the back projections of the object from 0 to 180 degrees to have enough data to reconstruct the object. To take these back projected and reconstructed the image from them a method must be used called forward projection. This technique takes the slice and angle and project the values at that angle across the image plane, as can be seen in Figure 3.
|
||||

|
||||
Figure 3: forward projection of basic image, with 6 projections
|
||||
|
||||
# Back projection
|
||||
As previously discussed with in the Background, to reconstruct and image firstly the image must be back projected to create a sinogram. Although this method will lose value due to polar sampling within the Fourier slice theorem. Using the reference image that was outlined within the Introduction, the sinogram of this image can be seen in Figure 4. The reconstruction from this sinogram can be seen in Figure 5.
|
||||

|
||||
Figure 4: sinogram of shepp-logan phantom
|
||||

|
||||
Figure 5: reconstruction of shepp-logan phantom
|
||||
This reconstruction is very blur and hard to make out the original image. The reconstruction also has a circle around it most likely from the forward projection stage where the image was rotated and stacked. However, the original shepp-logan phantom can still be made out although some of the smaller details were lost.
|
||||
|
||||
# Filtered back projection.
|
||||
The 2d Fourier transform of an image is defined in equation 30:
|
||||
$$
|
||||
f\left(x,y\right)=\int_{-\infty}^{\infty}\int_{-\infty}^{\infty}F\left(u,v\right){e}^{{j}\mathbf{2\pi}\left({ux}+{vy}\right)}{dudv}\ \ \ \ (1)
|
||||
$$
|
||||
This equation can then be rewritten using the projection theorem as seen in equation 1:
|
||||
$$
|
||||
{f}\left({x},{y}\right)=\int_{0}^{2\pi}\int_{0}^{\infty}F\left(\delta cos\left(\theta\right),\delta s i n\left(\theta\right)\right){e}^{{j}\mathbf{2\pi}\left(xcos\left(\theta\right)+sin\left(\theta\right){y}\right)}\delta{d}\delta{d}\theta\ \ (2)
|
||||
$$
|
||||
Simplifying equation 2 further gives equation 3, where p\left(\theta,\delta\right) is the projections of the image defined in equation 1 as f\left(x,y\right). And \left|\delta\right| is the defining the filtering that must be applied to each of the image projections.
|
||||
$$
|
||||
{f}\left({x},{y}\right)=\int_{0}^{2\pi}\int_{0}^{\infty}p\left(\theta,\delta\right){\left|\delta\right|{e}}^{{j}\mathbf{2\pi}\delta r}{d}\delta{d}\theta\ \ \ (3)
|
||||
$$
|
||||
Using this filter shape that was defined in equation 3, this filter is a ramp filter centering around zero. However, this is a first order filter, and a higher order filter can be used. Although these higher order filters tend to take longer to compute as well as have diminishing returns. This filter is convolved with each of the angle slices. This filter acts as an edge detection filter and increases the amplitude of the signal near the edges of the object. The output of this filter as applied to the first slice can be seen in Figure 6: pre and post filtered slice of the first slice in the sinogram.
|
||||

|
||||
Figure 6: pre and post filtered slice of the first slice in the sinogram
|
||||
Applying this filter to the sinogram as seen in Figure 4: sinogram of shepp-logan phantom, creates the sinogram in Figure 7 and reconstructing of this sinogram can be seen in Figure 34.
|
||||

|
||||
Figure 7: sinogram after filtering with ramp filter
|
||||

|
||||
Figure 8: reconstruction from the sinogram in figure 8
|
||||
The reconstructed image is sharper with more of the finer details showing through. Although within the image there still is some noise as well as a lack of contrast. The reconstruction also has the same circle around it as the non-filtered reconstruction.
|
||||
|
||||
# Analysis
|
||||
Comparing the reconstructions of the images as seen in Figure 5 and Figure 8, the filtered back projection creates a clearer image than the non-filter one. The reasoning for this almost bloom effect on the non-filtered image is the polar sampling in the Fourier slice theorem. This polar sampling causes the bloom effect as there are more samples near the center in Fourier space. These samples near the center correspond to low frequencies, so during the back filtering more lower frequencies samples are captured. This difference in the number of low frequencies captured compared to high frequencies causes most of the details in the image to be lost. These details get lost as these details tend to reside mostly within the higher frequencies in Fourier space.
|
||||
The filter image also shows a noise within the image. This is because the filter that was used is a ramp filter. This type of filter disproportionally amplifies the higher frequency in Fourier space. Although in this case where the higher frequency were the ones that were sample less, the effect is less noticeable. However, this noise could be reduced by using a higher order filter rather than this basic ramp filter.
|
||||
There are also circles that appear around both filtered and non-filtered reconstruction. These circles are more than likely to come from the forward projection stage when the image matrix is rotated. This would occur as during this stage the forward projected image is resampled as well as receiving some interpolation.
|
||||
|
||||
# Conclusion
|
||||
In conclusion not filtering the sinogram will lead to a blurry reconstruction with almost a bloom effect to it. although this effect was determined to be caused by the polar sampling of the Fourier slice theorem. Filtering the sinogram creates a sharper reconstruction with a small amount of noise. This noise was found to be from the ramp filter that as used to interpolate the high frequency values what was lost during the polar sampling. However, this noise could be reduced by using a higher order filter. There was also a lighter circle within the reconstruction, and this was determined to be from the interpolation caused by the rotation of the image matrix.
|
||||
|
||||
# References
|
||||
|
||||
[1] H. M. Gach, C. Tanase, and F. Boada, ‘2D & 3D Shepp-Logan Phantom Standards for MRI’, in 2008 19th International Conference on Systems Engineering, IEEE, 2008, pp. 521–526. doi: 10.1109/ICSEng.2008.15.
|
||||
[2] T. Salditt, Biomedical imaging: principles of radiography, tomography and medical physics, 1;1st; Berlin;Boston; De Gruyter, 2017. doi: 10.1515/9783110426694.
|
||||
[3] O. A. Godin, N. A. Zabotin, and V. V. Goncharov, ‘Ocean tomography with acoustic daylight’, Geophysical research letters, vol. 37, no. 13, p. n/a-n/a, 2010, doi: 10.1029/2010GL043623.
|
||||
[4]R. N. Bracewell, ‘Numerical Transforms’, Science (American Association for the Advancement of Science), vol. 248, no. 4956, pp. 697–704, 1990, doi: 10.1126/science.248.4956.697.
|
||||
39
src/content/blog/using-all-of-kicad.md
Normal file
39
src/content/blog/using-all-of-kicad.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: 'starting up a new kicad project'
|
||||
description: |
|
||||
this article covers the things i like to do at the start of a new kicad project, i will also properly add to this article more in the future with more of the cooler things i have found.
|
||||
heroImage: '../../assets/article_hero_images/kicad.png'
|
||||
pubDate: 'Mar 15 2026'
|
||||
---
|
||||
|
||||
KiCad is a suite for electronic design automation that allows the creation of both schematics and PCB for electronics projects.
|
||||
Over the pass 4 years of using this tool, reviewing other peoples designs within KiCad and just review more schematics I have become fairly opinionated around its use.
|
||||
And therfor this article will cover how to create a new kicad project and some simple first steps to take to create a good base for you to spring off of.
|
||||
|
||||
The first suggestions I have is during the start of your new project or even current project.
|
||||
within schematic setup (file->schematic setup) there are two different tabs I suggest you look at.
|
||||
"Field Name Templates" and "Net Classes". The former allows you to add additional fields to the symbol properties, i have found it is normally very useful to add 3 fields to this:
|
||||
- Supplier link : the link to where to buy the part
|
||||
- Supplier Part Number : the suppliers part number for the part very often differnt from the manufacturers part number
|
||||
- manufacturers part number : this is the part number itself normally KiCad libs will put this in the value field but i prefer a more explicit location for it
|
||||
- Notes: this field is useful for more basic things like the voltage rating of a cap, the ppms of a resistor or the colour of an LED, things that only matter when actually selecting the part not when creating the schematic
|
||||
|
||||
This will then give you these additional fields when adding a new symbol.
|
||||
Although during schematic creation filling these out will become tedious i prefer to fill out the notes field first just to mark down what im looking for then once the schematic is "complete" i will then proceed to actually add part numbers and links.
|
||||
Luckily once this is done you can ensure that the BOM you create will have all the part required for your design.
|
||||
|
||||

|
||||
|
||||
The former suggestion being net classes this field allows for the colouring of the connections within a schematic base of the name of the net class. For my most basic project I tend to colour the nets like this
|
||||
|
||||

|
||||
|
||||
Where the positive voltages are red negative voltages are blue and gnd is a lighter grey because it shows up better on the colour theme i use. This should then allow you to easily spot where power is within your schematic and should help you with working out if something need decoupling there or not.
|
||||
|
||||
The next thing i suggest is to add a housing symbol. although i know its very likely that your project is so small it doesnt need any form of housing this is still a great symbol to think about adding, as if you do need a housing it will show up within your BOM. This is in addition to also allowing you to assign a footprint to it that contains the edge cuts and mounting holes for the thing you are mounting in
|
||||

|
||||
|
||||
And last but not least when setting up a new KiCad project i always try to use hierarchical sheets within the design.
|
||||
This use of hierarchical sheets allows for you to not only think about your design within terms of functional groups but also easily show how these groups go together.
|
||||
|
||||

|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
title: 'Using MDX'
|
||||
description: 'Lorem ipsum dolor sit amet'
|
||||
pubDate: 'Jun 01 2024'
|
||||
heroImage: '../../assets/blog-placeholder-5.jpg'
|
||||
---
|
||||
|
||||
This theme comes with the [@astrojs/mdx](https://docs.astro.build/en/guides/integrations-guide/mdx/) integration installed and configured in your `astro.config.mjs` config file. If you prefer not to use MDX, you can disable support by removing the integration from your config file.
|
||||
|
||||
## Why MDX?
|
||||
|
||||
MDX is a special flavor of Markdown that supports embedded JavaScript & JSX syntax. This unlocks the ability to [mix JavaScript and UI Components into your Markdown content](https://docs.astro.build/en/guides/integrations-guide/mdx/#mdx-in-astro) for things like interactive charts or alerts.
|
||||
|
||||
If you have existing content authored in MDX, this integration will hopefully make migrating to Astro a breeze.
|
||||
|
||||
## Example
|
||||
|
||||
Here is how you import and use a UI component inside of MDX.
|
||||
When you open this page in the browser, you should see the clickable button below.
|
||||
|
||||
import HeaderLink from '../../components/HeaderLink.astro';
|
||||
|
||||
<HeaderLink href="#" onclick="alert('clicked!')">
|
||||
Embedded component in MDX
|
||||
</HeaderLink>
|
||||
|
||||
## More Links
|
||||
|
||||
- [MDX Syntax Documentation](https://mdxjs.com/docs/what-is-mdx)
|
||||
- [Astro Usage Documentation](https://docs.astro.build/en/basics/astro-pages/#markdownmdx-pages)
|
||||
- **Note:** [Client Directives](https://docs.astro.build/en/reference/directives-reference/#client-directives) are still required to create interactive components. Otherwise, all components in your MDX will render as static HTML (no JavaScript) by default.
|
||||
70
src/content/blog/web-revival-and-how-im-joining-it.md
Normal file
70
src/content/blog/web-revival-and-how-im-joining-it.md
Normal file
@@ -0,0 +1,70 @@
|
||||
---
|
||||
title: 'web revival and how im joining it'
|
||||
description: |
|
||||
the web revival is a concept around bringing back the social internet, an internet not controlled
|
||||
by big corporations and instead made by people and for people, this essay explains the aspects
|
||||
around it and what im doing to join it.
|
||||
heroImage: '../../assets/article_hero_images/web-revival.png'
|
||||
pubDate: 'Feb 21 2026'
|
||||
---
|
||||
|
||||
The web revival is a concept originally by [melonking](https://melonking.net/), it is primarily around not bring back the "old web" as such but more creating a new social web where people can create their own customizable spaces where they can be themselves.
|
||||
This customization is in direct opposition to modern corporate "social"[^1] media websites like discord requiring you to purchase a subscription in order to customise your profile, or the other sites like facebook, instagram, snapchat or twitter that force you to fit in to their (the corporate) idea of a user.
|
||||
In addition, these corporate websites are now even requiring you to send in your personal data for "children safety" when in all actuality these corporations never cared about the safety of children and are now instead being used as a tool to identify possible political targets.
|
||||
Not to mention all these companies also now using all your messages and post in order to train large language models so they can make more profit off you.
|
||||
But back to the main topic at hand the web revival movement as stated my melonking themselves "is not one single movement" and is instead a loose collection of multiple movement all loosely correlated with eachother. I have listed a few of these below with some examples with the majority of them coming from melonking's article (i have yet to get that much in to the weeds of web surfing)
|
||||
- the wild web:
|
||||
|
||||
the wild web is all about creating zines[^2] and art pages in a kind of chatoic punky style. Some example of this are:
|
||||
|
||||
- https://melonland.net/
|
||||
- https://n-o-d-e.net/zine/index.html
|
||||
|
||||
|
||||
- Neocities:
|
||||
|
||||
Neocities is a revival of the now defunct yahoo GeoCities where people can create and have someone host their own little website if they dont want to selfhost or use something like cloudflare or github pages.
|
||||
|
||||
- https://neocities.org/
|
||||
- https://nolove.neocities.org/
|
||||
- https://stone-road.org/
|
||||
|
||||
- Net Positive:
|
||||
|
||||
Net Positive is a movement around learning and the encouragement of others, this can be as simple as a recipe or as complex as how to create your own laptop
|
||||
|
||||
- https://32bit.cafe/
|
||||
- https://n-o-d-e.net/
|
||||
|
||||
- and may more youll just have to surf around and find them
|
||||
|
||||
but all these movements have several themes that tied them all together (melonking has a few more but these are the ones I think are the most important):
|
||||
|
||||
- make the internet a fun place
|
||||
- being creative, life is about having fun and bring joy to others do that
|
||||
- Right to Repair, just like what happened with geocities corporations can take away your ability to do something on something you purchased **stop them**
|
||||
- no to web3, web3 is an idea for a new web where AIs roam free and steal all your hardwork, your website is actually an NFT that some MBA just bought for 34 shitcoins just to resell. (i would include more about cryptocurrencies in here but I believe if they werent enshittified by big tech and scammer they would be just as welcome)
|
||||
|
||||
However this is only the first part of web revival creating the website, the second part is finding other people websites its like making your own house look pretty then not going to look at anyone elses this also help to create the social aspect of the social web. So my suggestion is to take a look at sites like https://melonland.net/surf-club , https://neocities.org/ , or https://onio.club/ .
|
||||
These are great places to start surfing the web and my suggestion to you is to start by adding a links page to your very own website to start getting connected.
|
||||
|
||||
Now why am I joining this movement, its simple it because everything it stands for is thing I already stand for. So to fully hop on the bandwagon my plan is to make this website more fun, "how" you ask, fuck if I know yet but thats the fun part a world of possibilities to splatter across your very screen when you next come back.
|
||||
But some fun idea I am thinking of implementing are, a guess book, maybe a little local chat, some simple javascript games and possibly a full rework to change this website from boring and 2d to a full 3d experience. to start with i have already included
|
||||
And finally to leave off with this microtheme, I believe a quote from banksy fits to this idea of the
|
||||
web revival almost perfectly admittedly it is a long quote
|
||||
> imagine a city where graffiti wasnt illegal, a city where everybody could draw what ever they liked, where every street was a wash with a million colours and little phrases, where standing at a bus stop was never boring, a city that felt like a party where everyone was invited not just the estate agents and barons of big business imagine a city like that and then stop leaning against the wall. its wet
|
||||
|
||||
|
||||
|
||||
## Additional links
|
||||
- what is web revival : https://thoughts.melonking.net/guides/introduction-to-the-web-revival-1-what-is-the-web-revival
|
||||
- doco for the banksy quote its right at the end : https://www.youtube.com/watch?v=QZFsbGpleX8
|
||||
- the video that first introduced me to the web revival idea : https://www.youtube.com/watch?v=tkUgOT22F5s
|
||||
- image used in hook from here : https://www.radiofrance.fr/franceinter/podcasts/net-plus-ultra/net-plus-ultra-du-vendredi-10-novembre-2023-4107010
|
||||
|
||||
|
||||
## footnotes
|
||||
[^1]: ive put social in quotation marks here as although these sites did originally focus on social interactions been people (hell I even used it to keep in contact with mates when I move to a different country) they now rely on pushing a feed of low quality slop to keep you engaged in order to pedal more ads to you. Nothing about that is social at all at this point
|
||||
[^2]: zines are small self-published magazines hence zine
|
||||
|
||||
|
||||
Reference in New Issue
Block a user