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.
| 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.
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