Placeholder Image

Subtitles section Play video

  • Hello.

  • In this video, I'm going to look at using polymorphism on object oriented programming to make our application development simpler.

  • I'm going to demonstrate that through the use of a simple computer aided design like application.

  • But you may have noticed I'm not in my usual programming place.

  • Well, it shouldn't be a very surprised you that I'm actually full time employees.

  • And sometimes my job requires me to go out on the road and stay in delightful hotels such as this one.

  • Nobody ever said programming was glamorous.

  • I'm traveling around with work like this has completely ruined my YouTube schedule for the year.

  • So those of you expecting to see on update to the top down city based car crime game Well, I might take something on at the end of this video if I've got a couple of minutes to spare.

  • However, that's been delayed a week.

  • So this video, I thought, be useful to do something to enhance sort of the c++ basics, and we'll look at polymorphism a very useful technique.

  • Now I thought to be different.

  • Instead of using games to demonstrate polymorphism, I'm going to create a small application, a very simple can't package.

  • So here I've got a matrix of dots which the cursor can clamp to when we can see we've got some axes on the middle of these axes represents 00 in our world.

  • Now, if I press a particular key, I could start to draw a shape and we'll see to little red dots that allow me to manipulate the shape so that we've got to box on here.

  • I can draw some lines just for them around all over the place.

  • At the moment on, we can draw circles to see the radius is a line surrounding by circle shape.

  • There's some other nice features.

  • Two I can zoom in and pan around.

  • Now, of course, we've done panning and zooming before, so I won't go into too much detail about how I do that in this video.

  • And I can also place my cursor over any of the red dots and tweak the shakes positions.

  • Oh yeah, before I forget, there's one more shape to this is a multiple construction shape that gives us a spine.

  • We can define curvature very nice, and you might be thinking, Well, what's this got to do with polymorphism?

  • well, it's difficult to graphically show polymorphism, but here all of these shapes are effectively the same object to the code underneath.

  • Of course, the shapes a unique in that they are circles and squares and lines and SP lines.

  • But the code that will handle these shapes doesn't really know about that.

  • And it doesn't care either, which means making our application can be quite a simple process because instead of trying to handle all of these different shapes, uniquely will let polymorphism do it for us.

  • Now, some of you not familiar yet with polymorphism may already be thinking of ways to handle this.

  • We could have lots of objects for all the different types of shape on our applications that interrogates those objects one by one, depending on how it should be manipulated or drawn to the screen.

  • Perhaps another solution is to create one type of object, which is all of the objects and the object itself in self determined what to do in a given scenario.

  • Approaches like this are valid, but they can be a bit messy on we may as well use the tools C++ provides for us to do this on dhe the primary tool and what this video is, that is.

  • Polymorphism will engineer our code in such a way that the compiler can decide at runtime which methods to call, what shapes to draw and how things should look.

  • Now I will start by saying that Polymorphism is not a beginner topic, although the way I'm going to show it in this video, we're really only going to start looking at it that the most top level, object oriented programming and polymorphism is a really rich and detailed topic in its own right.

  • But you can achieve quite powerful things just by knowing the very basics for this video, I'm going to assume you do know a little bit about classes already on the low Polymorphism does exist in lots of different languages.

  • I'm going to be specifically looking at C plus plus.

  • Let's stop with a simple class in this case called A and A has a function called Do Something.

  • What's the Do?

  • Something Function does depends entirely on what Class A is.

  • Now I'm going to assume that we have a set of structures in our program that's all quite similar in nature.

  • I they have a lot of commonality between them, so I'm going to try and package that commonality up in class A to turn it into a base class.

  • This, of course, means Aiken derive from my base class, More classes.

  • In this instance, Class B Class B inherits all of the methods and attributes from class A, assuming that it can do to whatever access specifies we've determined are on those properties off class A.

  • But let's for now assume that everything is public and easily accessible.

  • If I create an object of type B and call the do something function, then that do something function is defined by the base class, eh?

  • I can, of course, override the do something function by simply redefining it in class B.

  • Now, if I create an object of type B and call the do something function, it will call this one instead.

  • And it goes without saying that if I create an object of type A and call to do something function, it will call this one.

  • Perhaps I have another derived class.

  • Class C.

  • Well, exactly the same thing applies.

  • This too, could have its own do something function.

  • So far, so good.

  • Nothing out of the ordinary.

  • But we started this with a particular caveat on We said that these classes that derive from Class A were in some way similar on we created Class A as a base class to encapsulate the common functionality between the two divide classes on in applications.

  • It's useful to group things that are similar together.

  • And so how can we do this if I create an object of type Class?

  • A.

  • It is very clearly defined, was big of type A.

  • Likewise, if I create an object of Type B, it is very clearly defined as being an object of tight be.

  • It is not an object of type A, even though be inherits from a it is defined as being be naturally.

  • The same also applies to an object of Type C.

  • When we want to group things that a similar in C++, we have several ways of doing this.

  • One approach is, of course, to use an array.

  • But whenever we specify an array, all of the elements of the array of the same type, perhaps we could use a vector.

  • But again the same problem applies.

  • We have to nominate a type for our vector in front.

  • Any of the standard containers will have the same restriction.

  • The convenient thing to do is to hint to the compiler that B and C are in fact derived from a on that our vector.

  • Honore only stores elements off type A.

  • Well, this is quite nice, and we're almost there.

  • But if I wanted to use a specific element of our vector of Type A and call our do something function as we saw before, it's going to call the do something function defined by Class A.

  • We've still not got the flexibility implied to look at the divide classes.

  • That's because all of our objects have Type A.

  • Instead, I'm going to do something that appears a little strange.

  • I'm going to create an object of type Class A.

  • But I'm not just going to create an object.

  • I'm going to create a pointer to the object.

  • So this means now when I'm creating a new object, I need to use the new key word or smart point to equivalent.

  • But now I can do something a bit special because B and C were derived from a on All I'm storing is a pointer to where a might reside in memory.

  • I can actually create a new object B and of course I can do exactly the same for my Class C type.

  • So now when I create my vector toe, hold my similar objects together, I'll make it hold some pointers to Class A and threw some smoke and mirrors implemented by the C++ compiler.

  • What we are implying here is that if classes B or C override a method in class A, then if we were to call such a method in this case, our do something method that it will call the appropriate method related to the class that was constructed.

  • And that's quite a complex and powerful thing.

  • I can now store in a single container, a group of similar objects that all have unique functionality but a common interface in effect, we have allowed the base class A to morph into multiple subclass is a polymorphism.

  • We have eliminated the need to manually check what the type of each object is on.

  • Dhe perform.

  • The required functionality were requesting at that point in time, but it's not quite a simpler is.

  • I've just made it out to be we need to hint to the compiler which functions were likely to be overriding.

  • Here is a very simple class definition for type A.

  • We can see I've got the void do something method.

  • That's the one we've been trying to override with our sub classes.

  • If hours before, we just directly called the do something method on the object stored in our vector, regardless of whether that object was of type B or C, it would still call the do something method related to class A.

  • We need to tell the compiler that do something is a candidate to be overwritten by a base class on.

  • We can do that with the virtual keyword.

  • This hints to the compiler that I may be overwritten if I'm not overridden than any call to the do something function will naturally be implemented by the base class, eh?

  • Whatever code is written here, so if it is overwritten, it'll executes the overwritten code.

  • But if no override is provided, it will execute this default code and this one last philosophical thing to think about it, it may be the case that class A on its own doesn't make sense.

  • The sense only comes into it.

  • Once something has been derived from Class A i e.

  • We've created classes B and C, in which case Class A is really just an interface to its sub classes on its own.

  • It can't do anything useful on We'll see an example of that in this video.

  • Well, instead of providing some default code that doesn't do anything and to force the programmer to provide an implementation when anything derives from this class, we can tell the compiler that this class is an abstract encapsulation by defining our virtual method here as being pure, and you can't create an instance oven abstract class.

  • So if anybody now tries to create an instance of Type A, it will fail and throw an error to the programmer because no implementation of this function exists, even though something potentially can call it.

  • And so this is a very powerful and useful way off wrapping up your program structure.

  • Now I appreciate to some of you this may all seem incredibly alien, but don't worry.

  • I think the example code that's coming up really shows the power of this in a nice way.

  • So let's briefly take a look at the class structure for the cat program.

  • I demonstrated at the start.

  • I have a base structure called s shape and from that basic structure, I derive unique classes that represents the shapes that I have.

  • So a line, a box, a circle on a curve.

  • All of these things are indeed shapes, so it's very useful to recognize them as a singular shape.

  • But they look differently.

  • So I want each of these classes lined, box circling curve to be responsible for drawing itself.

  • So they will all have a method draw which draws it to the screen.

  • So it seems that draw is a great method to be phone into the base class.

  • The shapes are fundamentally defined by nodes.

  • Points in two D.

  • Space on the curve is but the oddball one here as it's defined by three points in space.

  • So all of my shapes are also going to have a common vector off type s node.

  • A simple structure on our node will contain the X and Y position of its location in space on a pointer to its parent.

  • And this is so when we select a node later on, we can immediately get access to what shape is it connected to a valid thing to shout out here would be.

  • But damn it, we're talking about object oriented programming.

  • Why are we not using classes?

  • Well, classes and strokes to the C++ compiler are absolutely identical, except for one thing.

  • And that is the default access method for a stroke.

  • All properties and methods.

  • Well, they're public.

  • And for a class, they're all private.

  • And so it really doesn't matter unless you're an object oriented programming enthusiast.

  • Naturally, I'm going to start by using a skeleton OLC pixel game engine program.

  • And if you've not heard of the pixel game engine, it's a very simple technology that I've created that allows you to draw things to the screen quite quickly and simply.

  • And I'm going to construct a pixel game engine quite a high definition.

  • One unusually.

  • This case is going to be 1600 pixels by 960 pixels, and there's going to be a 1 to 1 relationship between pixel game, engine pixels and screen pixels.

  • I have my traditional on user create an unused update functions on right now.

  • All on usurped it does is grab the mouse coordinates on the screen and stores them as a floating 10.2 d vector you saw in the preview at the start of the video that we can pan and zoom around the screen on.

  • I've already got a video.

  • It's just about this topic, so I'm not going to go into very much detail here.

  • In fact, I'm just going to bring that coded Onda converted to use our new vector type that we added in the last video.

  • I've also added a couple of other features to the pixel game engine, including the ability to draw dashed lines which will will see a little later on on that long last mouse wheel support.

  • The first thing I'm going to do is set this up to enable the panning and zooming and drawing of the axis in the world on.

  • None of this is new stuff.

  • In fact, I've done a video precisely about this topic, which you can click in the little Linka both but to facilitate panning and zooming, we need to store some world transformation variables on for this video.

  • I will be going quite quickly through this.

  • I've created numeric vectors that stole the position of the offset on DTH e start pound location.

  • We also need a variable toe handle the scale for the world transformation.

  • Finally, I'm adding a variable called grid on.

  • This is the spacing in world space between points that we're going to see on the screen, so all of my points are going to be one apart now.

  • Panning and zooming is all about transforming things from world space to screen space.

  • So the first functional at directly copied from the panning and zooming video and modified to suit the new floating point vector type is world to screen that takes in a vector in world space and gives me to screen coordinates as an output.

  • We also want the inverse of that screen toe world.

  • I'm going to set the default offset for our panning parameters to be the middle off this screen width and height.

  • So that puts 00 in world space in the middle of the screen.

  • Everything else is now calculated per frame in on user update.

  • Once I've got the mouse cord, it I'm going to enable zooming by scrolling the wheel but panning by holding down the mouse wheel and moving the mouse so I want to be sensitive to the mouse wheel being pressed, and that's going to store the start pan location.

  • As I have the button held down and move it around.

  • I want to update my offset.

  • So let's see quickly about drawing the world first.

  • I'm going to clear the screen on because I can see into the future, and I'm going to need some temporary interview coordinates to represent locations in screen space.

  • We only want to draw what we can see traditionally, so I want to work out what the visible extents of the world are on to do this very simple.

  • I simply use our screen toe world function with the top left corner of the screen on the bottom right corner of the screen, and this will give me the dimensions of the visible world in world space.

  • I want that to be a little bit of overlap around the edge of the screen, just so when we're drawing lines, you don't see them clipping, so I'm flooring the left and top values on.

  • I'm taking the ceiling value a rounding up the bottom and right values because I know the extent of my visible world.

  • I can simply have two nested four loops now that iterated along each access stepping in my grid size because now we're iterating through world space.

  • My grade was just set toe one, but I'll use the world to screen function to change this world space quarters into the screen space coordinates and use that location to plot a pixel safe in the knowledge that it doesn't matter where I am in the world, I'm only drawing what the little window of the screen can see into that world.

  • I also want to draw some axes into the world so I can see where my 00 location is.

  • And I'll do this Mike, extracting a line from world space based upon the visible extents of the world on, I'll capture those in screen space in the SX S Y E X and E Y variables, and it's simply a case of drawing a line between those two coordinates on.

  • I'm going to draw it in gray, but I'm going to just quickly and very sneakily show you a new feature that I've added to the pixel game engine, which allows you to draw lines with patterns on In this case, you can specify a different 32 bit pattern, and it will just simply change pixels on or off, depending on those bits.

  • So I want these to be dashed lines so I can do that by specifying this as the pattern, which says Four pixel should be on four pixel, should be off four on four off, four on four off et cetera, et cetera.

  • In this case, by clamping the ex recorded 20 I've extracted the information for the Y axis, and I could do something similar to extract the information for the X axis.

  • Let's take a quick look so that we can see we've got the axes going through the middle of the screen.

  • That's good.

  • That's my 00 location, and I'm gonna hold down the middle mouse button on.

  • Move the mouse around on.

  • The world is planning.

  • You might not be able to see the faint blue dots on the YouTube video, but they are.

  • They're switching over to full screen as shown in the panning and zooming video.

  • Zooming is a little bit more complicated.

  • The principal is that we look at where the mouse is before the zoom, Look at where it is after the zoom on, then translate this screen by that difference on that allows you to zoom in under the mouse cursor.

  • I either pixel that is under the mouse cursor will remain under the mouse cursor.

  • So I give you a very intuitive to you.

  • Zoom.

  • So here I've took my mouse coordinates on, worked out where my mouse is in the world.

  • I'm now going to respond to use the zoom commands on.

  • I'll do this in two ways.

  • Either they can hold the queue button down or they can scroll the mouse wheel.

  • And this is a new function to in this case, we're zooming in.

  • We want to scale things up, so we change our scale value by making it larger.

  • On the other hand, if the user presses the A key or scrolls the mouse wheel backwards, they'll zoom out.

  • And the reason I've included the keys as well is, if you're watching this on a laptop and you don't have a scroll button, you won't be able to zoom in and out very easily.

  • Once the scale value has been changed, we need to now calculate, were the mouse's again in world space and then translate the world by the difference between the two mouse positions.

  • Let's take a quick look.

  • So I'm planning on.

  • I'm changing my mouse wheel on.

  • You can't maybe see it very clearly if the blue dots aren't showing up what we are certainly zooming in on.

  • We zoom in relative to where the mouse cursor is on the screen.

  • Very nice.

  • I'm going to add an additional variable now.

  • Another factor floating point type called V cursor, and that's going to represent where the mouse is in the world.

  • But why is it different to the regular mouse cursor?

  • Well, in order to draw things accurately and cleanly, it helps if the cursor can snap to a particular location.

  • So in this case, I want the mouse cursor to snap to the nearest unit value or are good spacing in world space.

  • This is a very simple calculation to do.

  • This means that the user can't select anything in between the grid values.

  • Since we now have a Snapple cursor, I'm actually going to draw that on the screen by drawing a small circle around the snapped location on dhe.

  • I'm also going to display its coordinate via text using the drawstring function.

  • Let's take a look so we can see a little yellow circle representing the nearest grid point that the cursor is snap to effective.

  • I zoom in quite a bit.

  • You can see it doesn't move around continuously like the mouse cursor does.

  • It snaps to the nearest location, and panning and zooming works just fine, too.

  • So that's very nice.

  • It might even be worth wrapping up, panning and zooming into some sort of reusable container I can use in future videos and applications.

  • But I'm not going to do that today, So let's get on with programming our class structure.

  • We know that for this simple demonstration, I'm going to need a structure that represents a node on a structure that represents a shape.

  • Now our shapes are going to contain nodes and unknowns contained pointers to shapes, so I got a bit of a circular reference.

  • I can solve that quite quickly by Ford declaring my shape above the note are shaped, contains a vector of notes.

  • But because I've planned ahead, then you need to do that when working with polymorphism on object pointed programs I know that I want to specify what the maximum number of nodes is.

  • A particular derive shape can have for most of the shapes, it's to a line has a starting an end.

  • A box has a top left on the bottom, right?

  • A circle has a midpoint on a point on the circumference.

  • My shapes also going to have a color, and we'll start them off by them being green.

  • My shapes are going to be responsible for drawing themselves.

  • So I'm adding a virtual function in this case, and it's a pure virtual function.

  • I've put the equal zero at the end.

  • And so this is indicating to the compiler that derived classes from s shape must provide an implementation of the draw yourself function.

  • And in this case, it takes a pointer to the pixel game engine because I've defined their shape outside of the pixel game engine, I pass a pointer of it into the draw yourself function so it can access all of the familiar drawing tools we have available.

  • The user defined shapes by placing nodes on these nodes, belong to the shape that's currently being drawn, so I'm going to make it the case that the shape is responsible for creating the node on giving it to the rest of the application toe handle.

  • Appropriately, this is going to be the same for all shapes, so I don't need to make this method virtual.

  • It's called Get Next Node, and it takes in a position in world space, creates the note if it can, and returns a pointer.

  • To that note, the number of nodes shake and how is governed by the end.

  • Max nodes variable.

  • So if we've already reached that value, then we'll just return no pointer.

  • We can't create any more nodes.

  • The shape is complete.

  • For example, you can't have three nodes on a line.

  • You can only have two else.

  • If there is room for more nodes, then I'm going to create a new instance of a node and add it to my vector of nodes.

  • And now you'll notice something truly horrific, something I've done a video about telling you not to do.

  • I'm returning a pointer to the location of an element in a vector.

  • Very, very, very, very bad.

  • However, what we'll see in a minute is we can do this quite legitimately, providing We're quite careful on how we set up the vector in the first place.

  • Now all of our shapes are responsible for drawing themselves on.

  • They don't have access to the world to screen transform information.

  • But since this information is going to be the same at all times for all shapes, I'm going to create two static variables to this structure called World Scale on World Offset.

  • And I'm also going to provide a local implementation of the world to screen transform so the shape can actually do this transform for itself.

  • It doesn't need the screen to world War that doesn't make any sense.

  • The shape only exists within world space.

  • We know that our shapes are going to have derived classes that override the draw yourself function to distinguish themselves from each other circles and not drawn the same as lines.

  • For example, however, all of these shapes are responsible for drawing their own nodes, and this doesn't change per shape, so we may as well also include in our base structure the facility to draw the nodes of the shape, and this works in a very similar way will pass a pointer to the pixel game engine into a drawer nodes function.

  • It's a right through the vector of nodes.

  • Take the nodes location and transform it from world space to screen space and then draw a little red circle at that location.

  • We mustn't forget whilst I'm at the bottom of the structure that we've added to static members to this structure.

  • So we must initialize them.

  • And I just want to demonstrate that our s shaped structure is abstract by trying to create an object of it directly.

  • You see, it doesn't like it, and it says object of abstract class type of shape is not allowed.

  • Any goes ahead to tell us that the draw yourself function is a pure virtual function.

  • I can't find any implementation for it.

  • So we've protected ourselves from writing code that functionally wouldn't work.

  • I appreciate that this video moves a lot quicker than most of my others, but now we've got this set up.

  • We can start to see some of the benefits of thinking in terms of objects and polymorphism, so let's create the simple line structure my line structure inherits from my base shake structure, and I'm going to give it a constructor and in this constructor, we set the maximum number of nodes.

  • This shape can have two to the Lycan.

  • Have a start in an end.

  • Now, remember, before we did something which was an example of absolutely horrific practice, we returned a pointer to the element of the vector.

  • Well, Lance banned because vectors can move the contents around without you knowing so the pointer can become invalid.

  • However, we know how big that Victor's going to bay for a line.

  • There's only ever going to be two notes.

  • So I'm going to tell that director Go on, reserve two nodes worth of space, please.

  • This means the vector won't be reorganized beyond our control.

  • It's the same is declaring an array in this instance on, because the draw yourself function was abstract, We must provide an implementation for it in this derived class on weaken signify to our compiler that actually look hello.

  • We want to override this draw yourself function.

  • This key word is optional, but I think it gives you a nice little visible tick to say you are overriding a virtual function in a base class.

  • Drawing a line is quite simple.

  • We want to take the coordinates of the two nodes that we have in world space and convert them to screen space and simply draw that line well.

  • Now add to our application class appointed to a line.

  • Once you've handled all the pounding and zooming and snapping, we could start handling the remainder of the user interface for a line only want to create one if the user presses the Elke.

  • This signifies that we're going to create a new line.

  • I'm also going to add a pointer to Owen Node.

  • I'll call that selected node so it's any given point.

  • Whatever note is pointed to buy, selected node is movable by the mouse.

  • Cursor will default.

  • That, too.

  • No pointer.

  • I'm going to assume that the user pressed the Elke at the starting point of the line.

  • So I'm going to set my selected node variable to whatever is returned from the get next node function of the line shape, which, if you remember, turns the current location passed to that function as a note.

  • Since that's the first note placed, I don't really want to move that around with the mouth straight away, so I immediately want to get the second node because that is the one I'm going to move with the mouse As I move the mouse around.

  • Any node pointed to buy the selected note pointer, I'm going to set its position to my snapped cursor location.

  • As I click with the mouse, I want to continue placing nodes until the shape tells me there are no nodes left to place.

  • If the shape returns, no pointer.

  • When they get next node function is called, then the shape is complete.

  • I know no more nodes available to define it.

  • If the shape is complete, I'm going to set its cooler toe white.

  • Once I've drawn my dots and my axes, I want to draw my line.

  • So I'm going to call the draw yourself, function on the drawer nodes, function and pass in this reference, which is the pixel game engine into those structures so they can draw themselves.

  • But I don't want to do this if my line is equal to know Pointer.

  • Before we can draw any shapes, however, we need to set the static variables in our base shape class to set the scale on offsets for the world to screen.

  • Translation.

  • Let's take a look zoom in a little bit.

  • I'm pressing the LK, which has placed the first node, and it's also placed the second note.

  • But the second note is now attached to the mouse cursor, and the first and second nodes are used by the line shape to determine the start and end of the line so we can see it's green and it currently follows the mouse cursor around until I click and we have defined the line shape.

  • We've now almost finished the skeleton application we can pan and zoom on.

  • We have the facility to add a simple shape in this case, a line, but I think for application to be useful, we want to add multiple shapes, or at least multiple lines.

  • So I'm going to create a list to store our line shapes in.

  • Changing our application to hold multiple lines is actually quite simple.

  • I'm going to use a standard list to do this and create a new list called List of Shapes.

  • I'm also going to create a temporary variable called temp shape, which is going to represent the line that we're currently trying to draw on the screen.

  • So once the line has been confirmed.

  • It gets added to the list of shapes words before we created a new line directly.

  • Now we're creating a temporary shape.

  • We can determine what the mouse behavior implies by knowing if temp shape is set to Noel Pointer or not.

  • So if Tim Shape is not equal to know Pointer, then we're currently trying to draw a shape.

  • We're not just moving the mouse around on the screen, so if we're trying to draw a shape than as before, we see if there are any nodes available left for the shape.

  • If there are no nodes left than the shape is complete, so it's no longer temporary.

  • In fact, we want to push it to our list of shapes.

  • We also want to change the drawing code for our temporary shape.

  • Now the temporal shape is the instance currently being drawn, but we also have a list full of shapes that also need drawing.

  • So we'll do that before we draw the temporary shape by Iterating with a little auto four loop through our list of shapes.

  • Let's take a look.

  • So I pressed the L button.

  • We draw a line and click impressed the hell button we draw a line and click perfect lots and lots of lines.

  • I feel it's also very useful to be able to move the nodes once they've been placed.

  • So we need to put a routine into R S shaped structure to tell us if we've hit any of the nodes contained within that shape.

  • And this is a very simple addition.

  • Dysfunction hit node takes in a location in world space it.

  • Then it's a reach through all of the notes for this particular shape and works out to the distance between the nodes position on the position we've passed in.

  • If that distance is less than 0.1 we don't want to do a direct comparison because we're working with floating point numbers.

  • It would very rarely return true.

  • So it's better if we look at what the magnitude of the distances between those two points, if it's close enough, will return the address of the node that's been hit.

  • And we'll set our selected node pointer to the address of the node dysfunction returns.

  • If no nodes a hit, then we'll return.

  • No pointer.

  • I'm going to respond to the M key being pressed to signify.

  • I want to move the node that is under the cursor.

  • Firstly, I'll set the selected node to know.

  • And then for all of the shapes that I've currently placed, I'm going to reiterate through them and call the hit node function on my curse, a location.

  • If any of these return something that isn't no pointer, then I'm going to set that to my selected note pointer on break.

  • I found a note on Dive.

  • Now set it to something I can control with the mouse.

  • Let's just quickly try this out so I'll draw a line and I'll draw another line.

  • I'll put my cursor over the beginning of that first line and pressed the M key, and you can see I've now got appointed to that node, and I can manipulate that note with the mouse to try the other end of that line.

  • Very nice.

  • So if I make a mistake whilst I'm drawing my lines out, I could go back and fix it.

  • Our list of lines is all very good.

  • Aiken drawer manipulate unique lines on our canvas.

  • I want to add more shapes now so I could create additional lists to contain all of those shapes and a run time try and sort it all out by checking.

  • What type is each object, But I'm not going to do that, said I'm going to know exploit polymorphism.

  • And so, yes, it's been a little while coming with a bit of complicated code so far.

  • But let's get to it.

  • Let's turn this now into a polymorphic application here were restricted to objects that have type line.

  • But as we've been learning, we can use polymorphism, so I can actually sent this to shape.

  • And I'll do the same for our temporary shape.

  • Now what will look at is where we press the Elke to create a new line.

  • We know that temp shape is of type shape, but we're attributing to it.

  • A new object of type line on the compiler is completely fine with this.

  • If you don't believe me, a quick check.

  • It's working just fine.

  • So we have a list set up to store type of one object shape, but we're quite happily shoving into that list lines.

  • And in fact, as long as we adhere to the interface defined by shape, AII we provide something which implements this draw yourself function, we can add all sorts of objects to our list of shapes.

  • In fact, let's do just that.

  • Lets out the box shape.

  • So I'm going to start by just cutting and pasting the line shape.

  • I'll change it to s box.

  • I need to change the constructor Justus before the box is going to be defined by two nodes.

  • I don't want all of these comments again, so we'll reserve that in our vector of notes in the drawer yourself function.

  • Instead of drawing a line, I'm going to draw a rectangle where passing the top left coordinates on the width and the height, and that's an important distinction to make here.

  • It's not the bottom right corner.

  • It's the width in the heights, so you need to calculate that based on the Transformed world coordinates.

  • So I've created a very simple derive structure from shape called box.

  • Here was the code.

  • We responded to the Elke being pressed at the line.

  • I'm going to copy and paste that and instead of the l ke, I'm going to make it the bee key for box.

  • But this time, instead of adding a new line, we want to have a new box and everything else can stay the same are selected.

  • Node stays the same are manipulating the nodes.

  • So the same.

  • Our ending of the shape stays exactly the same and are drawing of.

  • The shape stays exactly the same.

  • Let's take a look.

  • It's us before we've got lines.

  • Now, if I press the Beaky, it draws a box.

  • We've added some quite different functionality to the application without changing very much of it.

  • On that change in functionality has been entirely encapsulated within the box structure.

  • Perfect.

  • We now have lines and boxes on These are unique shapes, unique objects that contained their own drawing code.

  • They've become encapsulated and self contained.

  • The nice thing about this is our application hasn't had to do any filtering of how to handle these shapes.

  • They simply draw themselves.

  • Are application, sees them is nothing more than just a shape.

  • And this is really the power of polymorphism.

  • So let's have some more shapes.

  • We've now got a very powerful pattern to which we can start adding all sorts of unique functionality.

  • Let's add a circle again.

  • A maximum number of nodes of two is going to be the midpoint of the circle.

  • On a point on the circumference, I'm fulfilling the draw yourself function.

  • Firstly, I can get the radius of the circle by just being the distance between the two points.

  • I'm going to draw a dashed line between those two points to represent that radius, and then I want to draw the circle itself.

  • But don't forget, we need to scale the radius by our world scale coefficient.

  • Here is our code for writing a line.

  • Here is our code for ending a box.

  • We'll just add in exactly the same code, but this time will create a type circle.

  • Let's take a look line box, circle.

  • And don't forget we can pick up the points and move them around when we can have lots of shapes.

  • This one here is a very, very small circle that we go.

  • Hopefully you can see this is a very powerful pattern now, so I'm going to have something a bit more sophisticated this time.

  • It's going to be a spine.

  • I'll call it Curve, and he's going to take in three nodes because this simple curve type has an end point.

  • A start point on a point in the middle with a curve moves towards.

  • I've done an entire video.

  • Siri's just about spines, so please check that out.

  • We know that when the user's first tries to create the spine, it'll have place to note on immediately also issued a second node, which they can drag around.

  • So if we've got less than three, nodes were drawing the first line fromthe starting point to the middle point.

  • If we have got three nodes, we've got enough information to draw both the structural lines of the spine, but also the curve itself.

  • So I'm drawing those structural lines as dashes, but I'm also going to then it's a rate with a small step along the curve and draw it out to the screen.

  • In fact, in this code, I'm breaking the curve into lots of little line segments on drawing those, so the curve shape is a little bit unique.

  • It requires more information in order to place.

  • It also draws itself differently, depending on how much information that it's got.

  • But this difference and functionality is entirely encapsulated within the curve shape.

  • We don't need to change any of our application in order to facilitate adding curves.

  • So I'll responds to the S Ky.

  • In this instance to add a curve spine.

  • Let's take a look pressed the S Ky.

  • So we've only got two nodes on the screen so far, so it's a structural line.

  • I click, then allocates the second note and starts to draw the curve and click again to complete it.

  • And so they have it.

  • A simple demonstration of using polymorphism to add features to an application without changing the underlying nature.

  • That's application a very powerful tool.

  • But as with all things object oriented programming, we really need to think about it in advance.

  • It's no good just jumping into the deep end on writing lots of code and hoping it's all going to come together.

  • That's called spaghetti Code on It will be early.

  • Maintainable on probably won't work, and you'll end up doing several really right.

  • So I really recommend sitting down and thinking about your application before developing it.

  • Anyway, All of the code for this video is available on Get Hope.

  • Please Downloaded have make some modifications at some new shapes.

  • Even if you've enjoyed this video, a big thumbs up, please have a think about subscribing.

  • Come and have a chat on the disco and hopefully I'll be back in my normal programming place next time.

  • Take care.

Hello.

Subtitles and vocabulary

Click the word to look it up Click the word to find further inforamtion about it