Added Complex Numbers extension - #612
Conversation
Adds my extension (salagata/reisen) Complex Numbers. Complex Number Type for do complex analysis functions, ~~better implementation than the one made by jwklong in Mathemathics extension lmao~~
Added a new entry for 'Complex Numbers' with detailed metadata.
✅ Preview readyYour changes are live at: https://zesty-maple-c0bb4c32.skr.mubilop.com/ Built from |
Added the new extension for complex numbers with improved implementation and additional metadata.
| const real = absolute * Degrees.cos(argument) | ||
| const imaginary = absolute * Degrees.sin(argument) |
There was a problem hiding this comment.
There work is pointless, as it gets discarded immediately when put into the constructor, which does this conversion on its own.
There was a problem hiding this comment.
This seemingly happens quite a lot in this extension when working with polar form.
There was a problem hiding this comment.
I think you're right in this one.
The constructor is supposed to receive 2 or 4 arguments, according the form of the number you're working with.
If you pass the absolute value and phase, if you don't pass the real and imaginary part, it will calculate it, if not, then it will borrow the ones passed.
But It seems like I did the fromPolar() function without thinking on that, I will fix it when I can
| if(z.length == 4) { | ||
| return new ComplexNumber.Type(z[0],z[1],z[2],z[3]) | ||
| } else { | ||
| return new ComplexNumber.Type(z[0],z[1]) | ||
| } |
There was a problem hiding this comment.
This looks like a case where spread syntax would be more readable.
There was a problem hiding this comment.
I thought here the code would be more explanatory but it seems like it might be a better idea to use the spread syntax.
Actually I was thinking to use spread syntax but...
| { | ||
| opcode: "polarToComplex", | ||
| text: this.formatMessage("[POLAR] to rectangular form"), | ||
| arguments: { | ||
| POLAR: ComplexNumber.Argument | ||
| }, | ||
| blockType: Scratch.BlockType.REPORTER | ||
| }, | ||
| { | ||
| opcode: "complexToPolar", | ||
| text: this.formatMessage("[COMPLEX] to polar form"), | ||
| arguments: { | ||
| COMPLEX: ComplexNumber.Argument | ||
| }, | ||
| blockType: Scratch.BlockType.REPORTER | ||
| }, |
There was a problem hiding this comment.
The should probably specify in the block name that they get stringified. Could also be combined into one block with a menu.
There was a problem hiding this comment.
Great idea, I haven't thought on it. I will use a drop-down menu for this one.
Well, I thought to use terms like "stringified" would look rare for people who doesn't know what "to stringify" is. Maybe I can add "as text" at the end though.
There was a problem hiding this comment.
The should probably specify in the block name that they get stringified. Could also be combined into one block with a menu.
almost no extension that converts stuff to strings specifies this
There was a problem hiding this comment.
Actually, this is common on first-class type extensions like these.
There was a problem hiding this comment.
i have literally never seen any extension do such a thing
There was a problem hiding this comment.
not a single one says it, the only blocks that say their name are for the purpose of stringifying, no other block that returns strings that isnt for the express purpose of stringifying says its type
The issue here is that these blocks are for the express purpose of stringifing. They're the only blocks in this extension that return strings, so they should specify.
converting != express "stringifing"
image removed
the one in the typeof should arguably return object and not string but it still returns string without saying anything, and it can be considered "first class"Well, yes. Obviously the extension only returns stringifies JSON isn't going to specify that it only returns stringified JSON.
thats not an excuse, your point that its common is just false
There was a problem hiding this comment.
thats not an excuse, your point that its common is just false
You yourself in the image above showed that it is common. There's quite a lot of precedent, the meer precense of which, in my opinion, means it's common, even if it not "the most common."
There was a problem hiding this comment.
converting != express "stringifing"
It is stringifing it, though. This extension uses a class that isn't a string by default and these blocks stringify it in a specific format.
There was a problem hiding this comment.
thats not an excuse, your point that its common is just false
You yourself in the image above showed that it is common. There's quite a lot of precedent, the meer precense of which, in my opinion, means it's common, even if it not "the most common."
its not, thats very few blocks out of thousands
There was a problem hiding this comment.
its not, thats very few blocks out of thousands
Thousands of blocks? Weren't we just talking about first-class extensions, not all blocks?
In any case, the fact that this extension does all of its computing in a non-text format makes, to me at least, it stringifying something important information. The extension you showed before (Mathematics) does its computations by passing around stringified JSON, it's already a string, so the information that it's being stringified isn't important, in this case, however, it being stringified is a) the entire point of the block, and b) something very different from the way the rest of the extension is handled, therefore it should say that it's stringifying the complex number.
| static fromPolar(absolute, argument) { | ||
| const real = absolute * Degrees.cos(argument) | ||
| const imaginary = absolute * Degrees.sin(argument) | ||
| return new ComplexNumberType(real, imaginary, absolute, clampAngleDegrees(argument)) |
There was a problem hiding this comment.
Not particularly important, but could you be a bit more consistent with your choice of terminology: argument vs. phase.
There was a problem hiding this comment.
I will stay with phase, but my mind sometimes makes me change it with argument.
I'll try to be more consistent.
| { | ||
| text: this.formatMessage("positive"), | ||
| value: "positive" | ||
| }, | ||
| { | ||
| text: this.formatMessage("negative"), | ||
| value: "negative" | ||
| }, |
There was a problem hiding this comment.
There really isn't a satisfactory way to label roots (because they're unordered), so I guess it makes sense you'd do this.
There was a problem hiding this comment.
I already know about the signs, but "Positive" and "Negative" are supposed to be the sign of the square root of the discriminant, the \sqrt{b^2-4ac} thing, the "Positive" and "Negative" are supposed to be the sign of \pm. Maybe i might change this for something... less confusing?
| }, | ||
| { | ||
| opcode: "squareRoot", | ||
| text: this.formatMessage("squareroot of [A]"), |
There was a problem hiding this comment.
either square root to sqrt but not squareroot.
There was a problem hiding this comment.
OOPS, my mind sometimes crashes at trying to remember the way to spell... well, square root in English. First I think it is "square root", then I remember "sqrt" but I need the long one, then I incorrectly think it must be "squareroot".
I will fix this error.
There was a problem hiding this comment.
The contrast on this is far too low: https://www.audioeye.com/color-contrast-checker/?foreground_color=D0D025&background_color=FFFE2E. Use a darker color, for instance, black: https://www.audioeye.com/color-contrast-checker/?foreground_color=000000&background_color=FFFE2E.
There was a problem hiding this comment.
Ah. I was running out of time, so this banner is a placeholder I will change whether I'm free.
Thank you for the advice!
| power(args) { | ||
| const A = ComplexNumberType.toComplex(args.A); | ||
| const power = Math.round(Scratch.Cast.toNumber(args.B)); | ||
|
|
||
| const firstReal = A.real, firstImaginary = A.imaginary; | ||
| let pair = [firstReal, firstImaginary]; | ||
|
|
||
| if(power == 0) { | ||
| return new ComplexNumberType(1,0) | ||
| } | ||
| if(power == 1) { | ||
| return A | ||
| } | ||
| if(power == -1) { | ||
| const u = A.real ** 2 + A.imaginary ** 2; | ||
| return new ComplexNumberType(A.real / u, -A.imaginary / u); | ||
| } | ||
|
|
||
| const absPower = Math.abs(power); | ||
|
|
||
| for (let _ = 1; _ < absPower; _++) { | ||
| pair = [ | ||
| pair[0] * firstReal - pair[1] * firstImaginary, | ||
| pair[0] * firstImaginary + pair[1] * firstReal | ||
| ]; | ||
| } | ||
|
|
||
| if(power < -1) { | ||
| const u = pair[0] ** 2 + pair[1] ** 2; | ||
| return new ComplexNumberType(pair[0] / u, -pair[1] / u); | ||
|
|
||
| } | ||
|
|
||
| return new ComplexNumberType( | ||
| pair[0], pair[1] | ||
| ); | ||
| } | ||
|
|
||
|
|
||
| squareRoot(args) { | ||
| const A = ComplexNumberType.toComplex(args.A); | ||
| const r = Math.hypot(A.real, A.imaginary); | ||
|
|
||
| return new ComplexNumberType( | ||
| Math.sqrt(1/2 * (r + A.real)), | ||
| (A.imaginary >= 0 ? 1 : -1) * Math.sqrt(1/2 * (r - A.real)), | ||
| ); | ||
| } | ||
|
|
||
| power2(args) { | ||
| const A = ComplexNumberType.toComplex(args.A); | ||
| const power = Scratch.Cast.toNumber(args.B); | ||
|
|
||
| if(power == 0) { | ||
| return new ComplexNumberType(1,0) | ||
| } | ||
| if(power == 1) { | ||
| return A | ||
| } | ||
|
|
||
| const r = A.absolute ** power; | ||
| const phi = A.argument * power; | ||
|
|
||
| return new ComplexNumberType( | ||
| r * Degrees.cos(phi), | ||
| r * Degrees.sin(phi), | ||
| r, phi | ||
| ); | ||
| } | ||
|
|
||
| nRoot(args) { | ||
| const A = ComplexNumberType.toComplex(args.A); | ||
| const subRadical = Scratch.Cast.toNumber(args.B); | ||
|
|
||
| if(subRadical == 0) { | ||
| return Infinity | ||
| } | ||
| if(subRadical == 1) { | ||
| return A | ||
| } | ||
|
|
||
| const r = subRadical == 2 ? Math.sqrt(A.absolute) : (A.absolute ** (1/subRadical)); | ||
| const phi = A.argument / subRadical; | ||
|
|
||
| return new ComplexNumberType( | ||
| r * Degrees.cos(phi), | ||
| r * Degrees.sin(phi), | ||
| r, phi | ||
| ); | ||
| } |
There was a problem hiding this comment.
You very much can do complex powers:
- assume you have complex numbers
$a+bi$ and$c+di$ $e^{\ln(a+bi)} = a+bi$ $\therefore (a+bi)^{c+di} = e^{\ln(a+bi)^{c+di}} = e^{(c+di)\ln(a+bi)}$ - since every complex number as a polar form, and the polar form can be used to find the natural logarithm:
$a+bi = r \angle \phi \implies \ln(a+bi) = r+\phi i$ (where$\phi$ is in radians) $e^{(c+di)\ln(a+bi)} = e^{(r+\phi i)(c+di)} = e^{rc-d\phi+(c\phi+rd)i} = e^{rc-d\phi}e^{(c\phi+rd)i}$ -
$\therefore (a+bi)^{c+di} = e^{rc-d\phi}\angle(c\phi+rd)$ (in radians)
There was a problem hiding this comment.
I was also thinking on doing complex powers, but I was running out of time and I a haven't thought it might be useful, also I've thought people can implement their own using the blocks that we already have.
There was a problem hiding this comment.
That's true. There is an e^ function in the functions block (operators, not this extension), but I'd expect the functionality to appear in an extension dedicated to complex mathematics.
| const positive = new ComplexNumberType(-b / (2 * a), Math.sqrt(Math.abs(det)) / (2 * a)); | ||
| const negative = new ComplexNumberType(-b / (2 * a), Math.sqrt(Math.abs(det)) / (2 * a)); |
There was a problem hiding this comment.
These are literally the same. I think you meant to add a minus sign infront of the complex part in the "negative" solution.
There was a problem hiding this comment.
Yes it is?. And, it is what is actually representing?
Why it looks like the same in your comment?
There was a problem hiding this comment.
Yes it is?. And, it is what is actually representing? Why it looks like the same in your comment?
what? no they are literally the same snippet copy-pasted, i know i dont know anything about complex numbers but i can certainly agree that -1 != +1 and vice versa for any and all dimensions of numbers
There was a problem hiding this comment.
AH SORRY, I was looking at the wrong line, I will fix it as soon as I can...
There was a problem hiding this comment.
And, it is what is actually representing?
I think you already know that the determinate needs to either be added or subtracted, in this case both are adding the determinate, resulting in the same value being gotten for both roots, rather than different ones.
There was a problem hiding this comment.
SORRY I COMPLETELY KNOW, I just got a bit confused?, I will fix it as quicker I can
There was a problem hiding this comment.
Sorry, I hadn't seen your reply to that while I was writing my own reply (GitHub for some reason doesn't load new review comments until you reload the page). I didn't mean to rush you.
| divide2(args) { | ||
| const A = ComplexNumberType.toComplex(args.A); | ||
| const B = ComplexNumberType.toComplex(args.B); | ||
| const u = B.real ** 2 + B.imaginary ** 2; | ||
|
|
||
| return new ComplexNumberType( | ||
| (A.real * B.real + A.imaginary * B.imaginary) / u, | ||
| (A.imaginary * B.real - A.real * B.imaginary) / u | ||
| , A.absolute / B.absolute | ||
| , untransposeAngle(-(A.argument - B.argument)) | ||
| ); | ||
| } |
There was a problem hiding this comment.
A bit more of a general problem I have with this extension, but having 2 blocks that do, from an outside perspective, identical things is a really bad idea.
There was a problem hiding this comment.
One is supposed to operate with the real and imaginary part divide(), the other one is supposed to operate with the absolute value and phase without being subject to the floating-point error at the moment of calculating the real and imaginary part divide2()
Having two blocks is for operate separately what do you want to keep from the original complex number, or whether you're using the rectangular or polar form (indicated with "using the polar form").
There was a problem hiding this comment.
What I mean is that throughout this extension there's a division (mind the pun) between 2 entirely separate systems that don't have much impact on how people using the extension will use it. Having the extension have several different blocks that seem to perform the same operation with often minimal (if any) difference in how they appear can be very confusing.
I think it'd be better if you didn't make much of a distinction between complex numbers originally defined in polar form and numbers originally defined in the form
There was a problem hiding this comment.
I will see if i do an alternative system, i.e. , maybe choosing the mode when you want to do the, i.e. division.
As I said, sometimes it can't be uncertain if people will use the polar or rectangular form, i.e., in programs where the polar form is often better for calculate, i.e. rotations. Or in graphs, where the rectangular form is better.
Steve0Greatness
left a comment
There was a problem hiding this comment.
I realize I haven't looked at the extensions.js changes yet, so here's a review there.
| code: "salagata/reisenComplex.js", | ||
| banner: "salagata/reisenComplex_placeholder.svg", | ||
| creator: "salagata", | ||
| tags: ["new","complex", "math", "graphics", "customtype", "utility"], |
There was a problem hiding this comment.
Graphics? Please explain what this has to do with graphics.
There was a problem hiding this comment.
Things where rotations are involved. Since complex numbers are perfect for rotations, rotating a complex number is just multiplying a polar number with an absolute value of 1 (r=1) and with a defined angle (\theta), instead of rotating vectors, they use A Matrix for rotate. The complex numbers are better for this kind of things involving rotation in graphics.
There was a problem hiding this comment.
I will see if i do an alternative system, i.e. , maybe choosing the mode when you want to do the, i.e. division.
As I said, sometimes it can't be uncertain if people will use the polar or rectangular form, i.e., in programs where the polar form is often better for calculate, i.e. rotations. Or in graphs, where the rectangular form is better
| }, | ||
| { | ||
| name: "Complex Numbers", | ||
| description: "Complex Number Type for do complex analysis functions, better implementation than the one made by jwklong in Mathemathics extension lmao.", |
There was a problem hiding this comment.
Descriptions are to describe your extension, for this, there's really no reason to mention another extension in your case.
There was a problem hiding this comment.
... sorry I just ... N-NVM forget I did that-
| code: "salagata/reisenComplex.js", | ||
| banner: "salagata/reisenComplex_placeholder.svg", |
There was a problem hiding this comment.
Just about directory arrangement: since your extension is under your own directory, you don't need to add a prefix to the filename (note that the ID should still have a prefix, just that the filename doesn't need one, the directory structure already essentially does that for you).
| tags: ["new","complex", "math", "graphics", "customtype", "utility"], | ||
| creatorAlias: "Reisen the Inaba", | ||
| notes: "Additional help by jwklong extensions", | ||
| unstable: false, |
There was a problem hiding this comment.
It not being unstable is assumed by default, so you don't need to (and in fact probably shouldn't) specify it.
Thanks for the advice, I will consider all of them for the next time I'm free |
There was a problem hiding this comment.
One last quick little note: extension thumbnails should be designed for a 2:1 aspect ratio (IE if you have 30 pixels in the width, your height would be 15), this current thumbnail isn't in that aspect ratio, so it doesn't quite appear correctly as of right now.
Notice how the top and bottom of the image are cut off. I'm aware this image is meant as a placeholder, but it's just a note for when you have a final thumbnail.
Steve0Greatness
left a comment
There was a problem hiding this comment.
Couple small things.
There was a problem hiding this comment.
For sure an improvement, but I'd personally switch the foreground and background colors; either that or darken the background a bit more (either-or, not both).
|  | ||
| The angles basically start in 0° but 0° is the top (when it should be in the right), then the front is 90°(when it should be in the top), which it's confusing. | ||
| The trigonometrical functions (even the ones defined in ([sin v] of ()) block), recieve as argument a different kind of angle. | ||
| Internally, Scratch has to do `90 - SCRATCH_ANGLE` in order to perform any trigonometrical operation. It converts the Scratch angle recieved commonly as input into a Plane angle(The one used in Mathematics). | ||
|  |
There was a problem hiding this comment.
You uploaded the images here, so they should be used in place of these clearly temporary uploads.
There was a problem hiding this comment.
Not really what I meant. I meant keeping the uploads here (on the gallery) and replacing the Catbox.moe files with them. But alright.
There was a problem hiding this comment.
you should have said that in a first place, waa
There was a problem hiding this comment.
Not really what I meant. I meant keeping the uploads here (on the gallery) and replacing the Catbox.moe files with them. But alright.
but in what file should i put them
because catbox.moe embeds are not loading for me
There was a problem hiding this comment.
should i make a new folder called complexDocs/ and inside put the files?. Because i tried to put them in the images/ folder but it gave an svelte scope error, something
There was a problem hiding this comment.
In the same location you put them in this PR before.
There was a problem hiding this comment.
In the same location you put them in this PR before.
I put them in static/images/salagata/complexDocs , but svelte didn't load them
There was a problem hiding this comment.
when i did that, static/images/salagata/complexDocs, it returned an svelte scope error, something like that. i'm not sure if put them in the lib/Documentation for avoid this error.
There was a problem hiding this comment.
i think i'm putting them in src/lib/Documentation
There was a problem hiding this comment.
Sorry for the late response. They do go in the directly you said before (/static/images/salagata/complexDocs), but on the server they're accessed through /images/salagata/complexDocs (without the /static/).
There was a problem hiding this comment.
Be very careful with your usage of files you find anywhere online. This file seems to come from Wikipedia under the CC BY-SA 3.0 license: File:Complex number illustration modarg.svg. Make sure to attribute this source in the documentation, not doing so is a violation of the image's license.
There was a problem hiding this comment.
How you attribute? You just say where you got it and who made it.
There was a problem hiding this comment.
This should be done on the docs page, by the way.
|
|
||
| While developing this extension, I found a problem in the way that Scratch represents the Angles (Internally, and in ANGLE inputs). | ||
|  | ||
| The angles basically start in 0° but 0° is the top (when it should be in the right), then the front is 90°(when it should be in the top), which it's confusing. |
There was a problem hiding this comment.
Rephrase suggestion:
In Scratch, 90° is pointing left, with 0° pointing up, which is different from the common representation which is the opposite.
There was a problem hiding this comment.
90 degrees points right tho, -90 is left
| In general. | ||
| ``` | ||
| COMPLEX_PLANE_ANGLE = 90 - SCRATCH_ANGLE | ||
| SCRATCH_ANGLE = -COMPLEX_PLANE_ANGLE + 90 |
There was a problem hiding this comment.
Generally, when negative numbers and addition are involved the same expression, it's often cleaner and more understandable to represent that relationship with subtraction.
SCRATCH_ANGLE = 90 - COMPLEX_PLANE_ANGLE
| description: this.formatMessage("Complex Number Type for do complex analysis functions, perfect for rotation where vectors are slow"), | ||
| color1: "#c3ba5e", | ||
| menuIconURI: "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxMjEuNjIxNjIiIGhlaWdodD0iMTIxLjYyMTYyIiB2aWV3Qm94PSIwLDAsMTIxLjYyMTYyLDEyMS42MjE2MiI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE3OS4xODkxOSwtMTE5LjE4OTE5KSI+PGcgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjAiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCI+PHBhdGggZD0iTTE3OS4xODkxOSwxODBjMCwtMzMuNTg0ODggMjcuMjI1OTMsLTYwLjgxMDgxIDYwLjgxMDgxLC02MC44MTA4MWMzMy41ODQ4OCwwIDYwLjgxMDgxLDI3LjIyNTkzIDYwLjgxMDgxLDYwLjgxMDgxYzAsMzMuNTg0ODggLTI3LjIyNTkzLDYwLjgxMDgxIC02MC44MTA4MSw2MC44MTA4MWMtMzMuNTg0ODgsMCAtNjAuODEwODEsLTI3LjIyNTkzIC02MC44MTA4MSwtNjAuODEwODF6IiBmaWxsPSIjYzNiYTVlIiBzdHJva2UtbGluZWNhcD0iYnV0dCIvPjxwYXRoIGQ9Ik0yNjIuMjMyNjksMTQ1LjA0NDA5YzAsNS4yMDA3NyAtNS4wMzI2Myw4LjYwNDQ2IC0xMi4zMTM3MSw4LjYwNDQ2Yy03LjI4MTA3LDAgLTEwLjc4NjUzLC0xLjEzNDE5IC0xMC43ODY1MywtNi4zMzQ5NWMwLC01LjIwMDc3IDQuNTc3NjgsLTkuODU5MjMgMTEuODU4NzUsLTkuODU5MjNjNy4yODEwOCwwIDExLjI0MTUsMi4zODg5NyAxMS4yNDE1LDcuNTg5NzR6IiBmaWxsPSIjZmZlYTAwIiBzdHJva2UtbGluZWNhcD0iYnV0dCIvPjxwYXRoIGQ9Ik0yNTEuNzQ3MDYsMTc3LjQ1Nzk4Yy0xLjY1ODEzLDMuNDYzNTggLTMuNTEyMzEsNi42ODI2OCAtNi45MDI3OCwxNC4zNjA4M2MtMy4wMTc0OCw2LjgzMzQ3IC0xMS45MjA0OCwyMi41MTA4MiAtOS45MDgzLDIzLjUxOTkyYzMuNTYwMzQsNS40MTE3MyA1LjgyMTY2LDQuODExMDMgMTMuNDkyMiw2LjU5Njk1Yy00LjE5Nzc1LDEuOTg4MDggLTIyLjAyNjk5LC0xLjUwMDUzIC0yNC41MTA2OSwtMS43MjgyNWMtMTEuNTg2NDcsLTEuMDYyMzIgLTQuNTg4MiwtMTcuMTg3OTQgMi45MTMxNSwtMzAuODg4MTJjMi4xNTEyLC0zLjkyODg4IDkuMzYyMTgsLTE1LjIyNjM4IDkuMTUzMzMsLTE4LjY4MTI3Yy0wLjQwNzg2LC02Ljc0NzExIC0xNy42OTM1OCwtNC44MzI4OCAtMTUuMzIxMTMsLTYuOTk0NzhjMi41MjM3NiwtMi4yOTk3OSAyMy41MTYyNiwtMC4xNzM0MyAyOC45MzMsMC43MzMxOWM0LjQzOTYyLDEuNDg3NjkgNi4yOTA0MSwxLjk2NjUyIDYuMjI5NTUsNC4xOTcyOGMtMC4wNzUxLDIuNzUyOTQgLTMuMTYzNzMsNi4yMjUxNyAtNC4wNzgzMyw4Ljg4NDI0eiIgZmlsbD0iI2ZmZWEwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+PC9nPjwvZz48L3N2Zz4=", | ||
| docsURI |
There was a problem hiding this comment.
You seem to have forgotten the documentation URL
There was a problem hiding this comment.
it's penguinmod docs URI, how do i include it?
There was a problem hiding this comment.
Take the name of the file that you put it as, remove the .md, then append it to https://extensions.penguinmod.com/docs/. That's how you know the URL.
|
please? |
|
sorry for not answering in a while, i was busy and github didn't show me the comments |
It's alright. I don't expect people to be able to reply instantly. |



I added my first new extension Complex Numbers
Complex Number Type for do complex analysis functions.
Ideal for things were rotation is involved like bullet hells, or simulating things like fluids, illumination or camps