Getting into the use of the curve class and the typically corresponding tube geometry constructor seems like the next step from creating lines. One nice thing about lines is that I am create them by making an array of vector3 class instances by making use of the set from points method of the buffer geometry class. However there are limitations with lines compared to what there is work with when it comes to mesh objects, so that leads one to look into the curve class and tube geometry.
I have a number of other posts that I have wrote on curves now such as my post where I am using curves to create and update custom geometry. I also started a threejs example post that is on a curves module.
I have the source code that I am writing about in this post also up on my test threejs repo on Github. This is also where I park the source code examples for my many other blog posts on threejs as well.
When I first started this post I was using r140 of threejs, and the last time I came around to do some editing of the post I was able to get all the examples working just fine with r146.
To start out with Curves in this section I will be going over some fairly simple getting started type examples of Curves. This will include some examples that make use of built in curve function examples, as well as at least one that has to do with creating a custom curve class.
There is a Line Curve 3 class that is one built in option for creating a curve. This is just making an instance of a curve that is just a line between two points, but it is still an instance of a curve. So then this can be used in any situation that i need a curve object that is just a line actually.
Although I can quickly create a curve that is a line actually I will typically want to use the Bezier Curve Built in Curve object function to create lines as well as curves in general. I make the kinds of curves that I will like to make as well as a straight line depending on the control point that I give when calling it.
When it comes to the curve and tube geometry constructors in threejs one has to start somewhere, so for this example I will be doing just that with the THREE.Curve constructor and the tube geometry constructor. For this example I am starting out with the usual features when it comes to things like setting up a scene object, camera, and renderer. In additional the the usual features I am also adding a light source as I will be going with the standard material for this example when setting up the mesh object that will use the tube geometry later.
When it comes to using the curve base class I will want to extend the base class and define a constructor as well as a get point method. It is in this get point method that I will be defining the logic that is used to create the curve. For this basic example I am just making as simple curve using the Math.pow method for the y values for each point, alone with just simple linear stepping for x values, and for now just leaving z values at zero.
Once I have a new class created by extending the base curve class I can then use this class to create a new curve object. This curve object can then be passed to the tube geometry constructor as the first argument for that constructor. The resulting geometry from calling the tube geometry class constructor can then in turn be used with a mesh object just like that with the various other built in geometry constructor functions.
When it comes to the Base Curve class there are some key methods of interest that are very useful for creating an array of Vector3 objects, or getting a single Vector3 object at any point along a curve. I touched based on this topic in the Basic section of this blog post, but I have to say that this is without question a topic that I should expand on with the curve class to say the learn about it.
There are a lot of reasons why I would want to create an array of Vector3 objects that are points along a curve, or get a single point along a curve. There is creating an instance of THREE.Points with a geometry that has a position attribute of vertices along the curve for example. When doing so there is not just the question of getting a number of points along the curve, but also the spacing between each point when doing so. The getPoints method of the base curve class can get a number of points along a curve just fine, but it will always be a fixed delta value between each point. With that said there is also the getPoint method that will just give me a single Vector3 object along the curve by way of a given alpha value. This getPoint method can then be used to address problems with this fixed delta problem with the getPoints method.
For this first example of Points and the base Curve class I will be using the GetPoints method to just get a fixed number of points along a curve. Once again this example is fairly similar to what I covered in the basic section all ready, but I would like to start off with an example such as this before moving on to more complex examples.
Once I have a curve class based object by any means I can call the getPoints method of the base curve class to get an array of Vector3 objects. Although this might work okay for many situations in which I might need to do something like this there is one draw back about using this and that is that the spacing between each point will be the same for each vector3 object along the curve.
Although the get points method of the curve class will work just fine for getting an array of Vector3 objects, there is still the matter of having custom spacing between the points when it comes to this kind of array of objects. So with that said often I will use the getPoint method over that of the getPoints method as it gives me greater flexibility with this. I can get the same result as with the getPoints method if I want by just giving a linear style alpha value for each point alone the curve. However I can also get any other kind of spacing that I might want by making use of various functions in threejs that help with the process of getting an alpha values, or using another other various expressions or means to do so.
For this example I am not just creating a static geometry with a curve object, but also updating the position attribute of that geometry over time as well. The set from points method of buffer geometry objects will work fine for making a geometry with a position attribute in the first place, but I have found that I will run into problems when using it to update a geometry over time.
The main thing about this example here is that I am using the get point method to get a Vector3 object for each point along the curve with a custom alpha value. I am not creating an array of Vector3 objects now, but rather directly mutating the state of the position attribute by making use of the setXYZ method of the buffer attribute class.
Then end result of all of this is then a situation in which the spacing between the points will change from something that is similar to the use of the getPoints method to a custom spacing that is created with the smoother step method of the math utils object.
There are a number of built in options for curves, many of which are 3d curves, but there are also a few that are very much 2d curves. For example there is an EllipseCurve that will create a 2d curve of an ellipse and it can also be used to create 2d Arcs and circles, in fact ArcCurve is just an Alias for EllipseCurve. There are also a number of built in curve options that are there to work with in both 3D, and 2D form as well such as CubicBezierCurve3, and CubicBezierCurve.
There are a number of little situations here and there where I might want to make use of a 2D curve rather than a 3D one. I often might be in a situation in which I will want to just have a simple 2D shape, and then make an extrude geometry of that shape. I will not be touching base on every little detail that might come up with 2d curves in this section as much of it might need to be a whole other topic in this post. However I think I should at least start out with some of the usual basic examples of 2d curves here.
One Basic use case example of a 2D curve might be to use one to create an instance of THREE.Shape. Once I have a Shape object I can then pass that to something like THREE.ShapeGeometry or THREE.ExtrudeGeometry to create a geometry that I can then use with a Mesh object.
The first step is to just create a 2D curve such as with the 2D form of the Quadratic Bezier Curve. Once I have the 2D curve I can then create an array of Vector2 objects by just calling the get Points method of the base curve class. This array of vector2 objects can then be passed as the first argument of the THREE.Shape constructor to create the Shape object. For this example I am then making a ShapeGeometry for a mesh object that will take a shape as the first argument. When using this geometry with a Mesh object I typically like to set the side option of the material to DoubleSide to make sure that both sides will render.
Another option for making a geometry to use with a mesh object with a 2d curve would be to make use of THREE.LatheGeometry. Think of a 2d object for a moment and then think of what happens when you spin that 2d object along on a axis, the end result is something that more or less looks like a kind of 3D object, and that is what the lathe geometry is all about. For this demo I am making use of the ArcCurve 2d curve class to create an arc. I can then use the get points method to create an array of vector2 objects that I then pass as the first argument of the LatheGeometry constructor, I can then pass another argument that is the number of segments that I want for the lathe geometry. The end result here is then a kind of Sphere like geometry, but with holes on the top and bottom of the geometry. I can then adjust the start and end radian values to close or expand the holes.
Often I will find myself in a situation in which I will want to make not just one curve, but a whole bunch of them, and link them all together into one sort of logical path. With that said there is the built in curve path class of threejs that can be used to create this kind of curve of curves. I can then use the get point method to get a single point along one of these curve paths. However if I want a collection of Vector3 objects I might want to go with the get spaced points method of the curve path class.
One way to start out with these curve paths would be to create a collection of THREE.Points by using the getSpacedPoints method of the CurvePath class. However before doing that I will need a CurvePath to begin with, with at least one curve added to it. So there is just calling the THREE.CurvePath constructor to get a new CurvePath to start things off. Once I have the curve path stored to a variable I can then start adding other curves to it by just simply calling the add method and passing a curve that I would like to add to it. For this example I am just making use of some line curves and a single Quadratic Bezier Curve.
One thing that will come up all the time with threejs, as well as with computer programming in general is the concept of what is often referred to as an alpha value, at least in terms of threejs docs terminology at least. What I am really taking about here is a number in the range of 0 to 1 which can take on many names. Other terms for this kind of value might be things like Unit Interval, normalized value, I often referred to them as percent values they are similar to that kind of range.
There are a whole lot of functions in various classes in threejs that expect one of these alpha values as an argument. In fact thus far in this post alone I have all ready covered a number of demos that use the getPoint curve class method that takes an alpha value as the first argument. There are a number of various typical expressions that can be used to create these kinds of values, also there are a lot of methods in the Math Utils object of threejs that help with these as well. However in this section I am going to explore using curves as a way to create these kinds of values.
For this first demo of the alphas section of this post on curves in threejs I will want to work out at least some of the basic things that will be necessary to do this subject justice. I have a create curve alpha funciton helper funciton that will return a get alpha funciton that uses a given curve. I then also made a draw curve graph helper funciton that will draw to a 2d canvas drawing context the current state of a given get alpha funciton.
There are all kinds of things that I can do with an alpha funciton of course, but for this example at least I am just using it to set the scale and rotation of a mesh object.
In the math utils object of threejs there are a number of methods that can also help with getting smooth alpha values for various kinds of effects and transitions. Many of these work nicer than just having a simple linear like translation, however what is really nice about using curves as a way to create these kinds of functions is that it allows for custom movement that is just about any kind of effect that I would want when it comes to creating these kinds of functions.
When I first started writing this post the main over all subject of the post was to extend the base curve class to create a custom curve class. This is then a number of older demos that have to do with making a custom Sin Curve class by extending the base class. They also have a lot to do with the TubeGeometry constructor. In time if I keep editing this post a little more now and then I might eventualy removed these demos. However for the most part I often prefer to just keep pushing older demos such as this down to the bottom of the every growing content.
So now that I have a basic example out of the way that involves creating a custom curve class by extending the base curve class out of the way I think I will want to have at least one more example that involves something like a spiral of sorts. I then came up with this custom sin curve class as a way to further explore just making custom curve classes. I also wanted to start working out at least a few basic demos of the tube geometry constructor as well while I was at it with this sort of thing.
Thus far I just have some static scene examples of this curve class out of the way, so then I should have at least one example that involves an animation loop then. One way to go about doing this might involve creating a new curve object with update arguments in the body of the loop that I can then use to make an updated geometry. In can then make use of the copy method of the buffer geometry instance in the mesh object to copy this update geometry to the geometry object instance of the mesh objects as a way to update the geometry.
The use of the copy method might now always be the best way to go about updating geometry over time though. I have found that often the best way to update the state of geometry often involves mutation of the various buffer attributes of a geometry rather than copied a new one over each time.