字幕表 動画を再生する
[BELL RINGING]
Hello, and welcome back to part 2
of building your own Processing library in Java.
Look, I'm working with Eclipse!
I love eclipse!
All of you will complain that I'm using Eclipse,
and I don't care.
I love Eclipse.
It's one of the first places I learned to program,
so it has a special place in my heart.
So what I did in the previous part--
you're probably going to want to go back to watch it
if you're here and you didn't--
was set up the development environment,
so install Eclipse, make sure I have Processing installed,
setup all the paths, the build paths, the project,
the template, the cloning, all of that stuff.
So what I am here right now, I successfully built the library.
So it shows up now in Processing under Import Library.
And, remember, I'm choosing OpenSimplexNoise as the topic.
It's very arbitrary, but I want to build something
that's actually useful and can be published as a real library.
But the library is here, but if I click this,
it automatically adds the import statement,
which you can see is just template.library.*.
So in this video, what I want to do is code the library itself
and make one example that uses it.
Probably the hardest part of making a library
is being thoughtful about the design
of the library API itself, how you make the examples,
as well as documenting it.
So that's more of a broader topic related
to how to maintain and publish an open-source library,
but I'm going to mostly focus on the technical details
of building the thing.
And maybe towards the end, I can circle back and talk more
about those key important concepts and topics
to having a healthy open-source project.
So first thing that I want to do,
if I want to customize this, it I
should say something like "import
shiffman.opensimplexnoise," or something like that.
I mean, I don't think that's what I'm going to call it.
I'm going to go back to Eclipse.
I'm going to go over here, and I'm looking for under Source.
And so, look, this is what's known as a package,
template.library.
And so, because the package name in the template
is just "template.library," that's what the import was,
template.library.
So I'm going to right-click on this,
I'm going to go to Refactor, and click Rename.
And then I'm going to come here, and I
got to pick a name for it.
Let's call it "algorithms.noise."
Ultimately, I think what could be exciting
about this library is to implement
a bunch of different noise algorithms, OpenSimplex
noise, fractal noise, gradient noise, Worley noise.
So many different kinds of noise algorithms
could be in here that would be additions and enhancements
to the built-in, original Perlin noise algorithm that's
in Processing itself.
So that'll be my name.
I'm going to click OK, excellent.
Now I want to go into the source code, which
is called "HelloLibrary."
So I am going to rename this also
by going to Refactor, Rename.
And I'm going to name that--
let's call this particular Java class "OpenSimplexNoise."
And I'm going to do Finish.
OK, great.
I'm going to go over here, and there we go.
Let's try rebuilding the library.
We don't need to rebuild it at every single stage,
but let's just rebuild it to see if what I did at least
shows up with the proper naming conventions.
So I go to Window, Show View, Ant.
Click here, and do Ant Build.
BUILD SUCCESSFUL is what you're looking for.
Go back to Processing, Sketch, Import Library,
OpenSimplexNoise.
There we go!
Interestingly, it's giving me an error message.
Let's try restarting Processing and see what happens.
All right, I restarted Processing,
and the error message went away.
So certain things that you change,
if you're changing the package name,
even though the new library files
are being built and brought over into Processing,
Processing has loaded what it thought all the package
names were when it started up, so you have to restart.
So I don't think with every single change
you make while you're building the library you're
going to have to restart Processing,
but that certainly is something you'll
have to do from time to time.
One of the things I like to do when
designing an open-source library is actually
write the example first in the way
that I imagine that it might be.
So even though I haven't built any
of the functionality of the library itself,
what I want to do is I want to take a simple processing
Perlin noise example that uses just
the built-in original Perlin noise from the 1980s algorithm,
and then change that example to use OpenSimplex noise.
So if I go to Examples, under Basics,
under Math, let's just take Noise1D,
and let's look at this example.
This Noise1D example is mapping the x location
of this circle it's drawing to the noise
implementation in Processing.
So I'm going to grab all this code,
copy it, close this out, and paste it in here.
And I'm going to imagine what I might do.
So I might do something like, OpenSimplexNoise noise.
And then I'm going to say "noise is a new OpenSimplexNoise."
So presumably the idea is that OpenSimplexNoise
would be an object inside of the package algorithms.noise.
Maybe I want to name that just "OpenSimplex,"
because by definition it's noise.
Who knows, but I want to create a new instance of it.
Maybe, optionally, there could be
a seed that goes in here, because I could
be randomly seeding the noise.
And then, what I would do is, say--
(CHUCKLING) and so let me call it something different
for it to be less confusion.
Let me call it "osnoise" for OpenSimplexNoise.
And then, probably what I would want to do is say
"osnoise.value," pass it this X offset.
And, actually, that's it!
Because, really, this is such a like--
even though the OpenSimplexNoise algorithm is quite complex,
I just want one function that gives me the noise value back.
And I'll call that value-- or, I could have called it "noise."
I don't know what to call it.
But, again, these types of naming an API design decisions,
while incredibly important, I'm going to maybe tackle later,
and through user testing and discussion,
and are less crucial right now in terms of the nuts and bolts
of how to build your library.
How to think about making an open-source library,
it's so important!
But right now I can just pick something.
Let's pick value.
Let me go back to the library code.
And I'm going to add a function now.
I'm going to add a function called "public value."
And it's going to receive an argument, like X offset, xoff.
And I'm going to say "return negative 5."
So I just want to test out this idea.
I want to add a function called--
oh!
This should be "public float value."
Because it's a Java function that returns a float.
It receives a float.
So, again, this is not the OpenSimplexNoise (CHUCKLING)
algorithm, but I am just going to have it return
negative 5 to see if it works.
And let's actually just give it the number 50.
Well, actually, noise should return a value between 0 and 1
or, in this case, maybe between negative 1 and 1.
So let's actually make it negative 0.5.
And, in Java, if I want it to be a float,
I've actually got to add that f to indicate float.
So, once again, I am going to build the library.
The library is rebuilt. Let's go back to the code.
Oh, wait, I'm getting an error.
"The constructor OpenSimplexNoise noise
is undefined."
Why am I getting that error?
Because I have a constructor OpenSimplexNoise--
oh!
It needs this thing, PApplet theParent.
Ah-ha!
What this is and how this works, PApplet theParent,
is probably something I should tackle or address
in maybe a third part or a fourth part to this series
because I don't think that I need it
for this particular OpenSimplexNoise library.
But this is a way of having the library know something
and communicate to or execute functions in the user's sketch
itself.
It's not something I initially need for OpenSimplexNoise,
but let me just keep it in here for right now.
I'll decide later if I can remove it.
But there's-- this is not a flaw of the library design.
It's now a flaw of my code where I need to say "this."
The keyword "this" in Java refers to "this class,"
and your Processing sketch is actually
a class that's just hidden from you when you're
working in Processing itself.
So now let me run this.
And we could see the negative 320.
Why is it negative 320?
Because I'm multiplying it by width.
So that's right, width is 640.
The only noise value I ever get back
is negative 0.5, multiplied by width is 320.
So now we're building the library
and we added our own function to a particular class
in the library itself.
The next step is let's actually put in the OpenSimplexNoise
algorithm.
Here is the r-- it's raw source code for Kurt Spencer's 2014
implementation of OpenSimplexNoise in Java.
Now, there's a little bit of an unfortunate
thing, which this class is also named "OpenSimplexNoise."
Again, here I am back to this naming thing.
Everything is quite difficult, but let's--
let me first just bring this into Eclipse.
So there's a variety of ways I could do that,
but I'm just going to copy paste it.
So I'm going to make-- here, I'm going to create a new class.
And I'm going to call it "OpenSimplexNoiseKS,"
the KS for Kurt Spencer's.
I want to keep that implementation entirely intact.
So I'm going to hit Finish.
I am going to paste it in, and then I am going to hit Save.
And then, I need to change this to KS.
I need to add the package declaration.
And then, I need to find other things.
Like, this should be KS, this should be KS,
this should be KS, this should be KS.
There we go.
Now that I have the OpenSimplexNoise original
implementation from Kurt Spencer as a separate class
here in my library, I have a decision to make.
Ultimately, what I want is for that functionality
to appear in my OpenSimplexNoise class.
And so I could extend the OpenSimplexNoiseKS class,
that would be known as inheritance.
Or I could really just wrap it by making an object in here,
like OpenSimplexNoiseKS generator that
is an instance of Kurt Spencer's OpenSimplexNoise
implementation.
And this is referred to as composition,
maybe like wrapping another class as an instance
inside of a class.
So the reason why I want to do this is I'm trying to--
I'm not trying to expose the full implementation
and just add a few window dressing things to it.
Although, that would be a legitimate way
of designing this library.
But I'd rather do it this way just to really hide that,
but have my own set of functions that make use
of the implementation there.
So I'm going to do that here.
And then, so in the constructor, I
need to create a generator equals new OpenSimplexNoiseKS.
And then, in this value function--
and I don't need this say hello function.
The welcome function is maybe nice to have.
This set-- I'm going to get rid of the set variable
and get variables.
Those are all just template functions.
But what I want to do now is, instead
of returning to some arbitrary number,
I want to return the generator.
And the function inside of the OpenSimplexNoise
is called "eval."
So maybe I'm actually going to call this "noise."
And the function's called "eval."
So I'm going to pass X offset to eval.
(CHUCKLING) The thing is, the eval--
the OpenSimplexNoise implementation
doesn't allow for one dimension.
But for me to get one dimension, I
could always just pass as the second argument a zero.
So why do I have an error here?
Let's look at what this error might be.
"Cannot convert from double to float."
Oh, right!
Because OpenSimplexNoise implementation
is all done with doubles, but processing and simplification
only works with floats.
So here's where I can now change this to be a float.
And there we go.
So now I have essentially wrapped
the general-- the eval function, which works in two dimensions,
to work in one dimension with floating-point numbers.
And now, if I rebuild the library, go back to Processing,
and I'm going to run this.
Oh, and it's called "noise" now.
I renamed it from "value" to "noise."
So this circle is disappearing off the screen
because I'm getting negative numbers.
The built-in noise implementation and processing
always gives you a value between 0 and 1,
where as this OpenSimplexNoise implementation gives you
a value between negative 1 and 1.
So there's probably some advantages
for me keeping that range between negative 1 and 1.
But if I want to make the case that what
I want people to be able to use this
for is to have their current Processing code just work out
of the box by changing it to the OpenSimplexNoise function, then
maybe what I should do is, here, I should actually say--
I'm going to say--
I'm going to just call a remap.
I'm going to make a function called "remap," private float--
or, double remap, double val.
And what I'm doing is I'm saying "return val plus 1 times 0.5,"
right?
Because a value between negative 1 and 1
would shift between 0 and 2, divided by 2
would shift between 0 and 1.
So I'm going to do that.
So I could rebuild and test this,
but I'm pretty sure that correction will work.
Let's now add functions for 2D, 3D, and 4D noise
to make sure those work as well.
So I'm going to do this.
(CHUCKLING) I'm just going to do this a few more times.
Then, I'm going to add a Y offset.
I'm going to add a Y offset and a Z offset.
And then, I'm going to add a Y offset, a Z offset,
and a U offset.
And so this would be xoff, yoff.
And, again, I could refactor this to probably
have them call each other, but I think this
will work fairly well for me.
And then, here.
So now we have 1D, 2D, 3D, and 4D noise.
And I also should add a constructor, which
allows me to pass in a seed.
So I'm going to say "int seed."
And I'm going to pass that seed to OpenSimplexNoise.
And with no seed, maybe I'll change it to just
call the other constructor "this" with theParent
and a System.currentTimeMillis.
So I'll use that as the seed.
Ah, so I have an error here.
Whoops.
Oh, it's because currentTime.Millis
is a long data type.
So maybe I'll change this to long.
I think that's probably OK, because you can always
give it an integer.
And so there we go.
So now I have two constructors.
If I just say "OpenSimplexNoise this," it'll pick a random seed
or I can give it a specific seed,
and it'll generate with that seed.
So now I'm done [CLAPS] with the basic functionality
of this library that I wanted to build.
So let's go back and build it again.
So people who are watching this live right now in the chat
are rightfully complaining about my redundant duplicated code
here.
At a minimum, let me refactor this to just have the first 1D
noise function.
Just return this.noise X offset, 0.
I do think that the implementations, as you
get to higher dimensions, run more slowly.
So I don't actually want to call those with fixed dimensions,
but I only want to do that for the first one.
So, again, I'm sure there's a way
to optimize or refactor that.
It'll come later in the building this library,
but I just want to get it to work.
Let me rebuild the library one more time.
Go back to Processing, and run it.
There we go.
The same exact example, but now with OpenSimplexNoise.
I'm going to hit Save.
And I'm actually going to now make this one of the examples.
Whoops.
So I'm going to call this, "OpenSimplex--
"OSNoise1D."
And then, I'm also going to save this as "OSNoise2D."
Let's also go and grab Noise2D.
And we're going to Sketch, Import Library,
OpenSimplexNoise for Processing.
I'm going to make a OpenSimplexNoise noise
instance.
I going to say "noise equals new OpenSimplexNoise noise this."
Then, here I'm going to say "noise.--
oh, let's call this "OSNoise."
And I'm going to say, osnoise noise.
And let's run this.
Look, the same exact example, but with OpenSimplexNoise!
Change this to regular noise and run it.
It looks like this.
Change this to "improved."
What I would say is improved OpenSimplexNoise,
and it looks like this.
So that's another example.
And so now, you'll see, by the way, in Eclipse,
you can see that the examples are showing up here.
And I can get rid of this hello example
because I don't need that.
I finally have a fully functioning, working version
of the library with two examples.
Here's the thing.
There's a lot more work for me to do.
I haven't added anything in the code
comments that will help generate documentation for the library.
I haven't published a library in a way
that it would show up here under Import Library, Add Library
in this actual list here.
This is a way of publishing it to the Processing
list of libraries itself.
But this is a good stopping point,
because now I'm at a point where you, the viewer,
could actually use this library.
So I will come back and do a third video
about cleaning up and finalizing and publishing
the library in a more public way.
But right now what I'm going to ask of you, the viewer,
is two things.
Number one, go and download and try this library yourself.
So I'm going to compress this folder
and make a file called "OpenSimplexNoise
for noise Processing.zip."
And I will add a link to the GitHub repo.
Here is the GitHub repo for the library.
If you go here, the link to this is in the video's description.
By the time you're watching this,
there will be more information in this README about how
to get your hands on this zip file
and manually install it to your Processing download.
So you can test the library.
You can give me feedback by filing an issue here on GitHub.
No issues have been filed yet.
And you could also contribute to the functionality
of this and documentation of this library.
So if you have some time and would
like to work out filling out this JSDoc,
information inside the source code itself.
If you really don't like the way (CHUCKLING)
I've written these four noise functions
and want to refactor that, we can
think about and have a discussion about the API design
decisions.
And after I let that cook for a little while,
I'll come back and do a third video
to show you these updates, what kinds of changes I've made,
and how to take that final last step to creating a website
and publishing your Processing library to a website,
and having it appear in the Processing contributions
manager itself.
All right, [CLAPS] I hope this has been helpful to you.
I look forward to seeing this project grow.
We can add some other noise algorithms to it.
There's going to be a lot of fun in creating
this open-source project as a Coding Train community
and adding it to Processing itself.
Thanks for watching, and I'll see you sometime soon
in the third part to the series.
Goodbye!
[TRAIN FLUTE SOUND]
[THEME MUSIC]