1. Please write a c++ program and show all outputs. Curves, Lines, and Points No
ID: 3856892 • Letter: 1
Question
1. Please write a c++ program and show all outputs.
Curves, Lines, and Points
No matter how complex a (2D) geometrical shape we have, it can be described in terms of curves and lines—both of which rely on the concept of a point. Since curve drawing can be more complex (and thus might require a stronger math background) than line drawing, we'll exclude curve support in this course. But, being (hopefully) firm believers in reusability, we'll make sure that adding support for curves won't later require a massive redesign. To do this, some basic understanding of how one might support arbitrary curves will be required. This knowledge is necessary to understand the future of our system, not just the present. When creating software that you intend for reuse, you must think ahead. Even if you can't predict the future, you can often make room for it. In our case, we can't (at least without taking the time to research intensely—at which point we might as well implement) predict exactly how the curve drawing algorithm would be implemented, but we can understanding some simple ways of defining curves.
In figure 1.1, a curve is described by two additional points at a given point. Since these two additional points only have value in their relation to the first, they are relative. We will visit relativity in detail in Lesson Two, but you can think for now of the additional points as being relative to an origin of the primary point. To differentiate these points from the primary point—the one that actually defines where the line passes through—we will call the relative points handles.
With more analysis, you will see that these handles are quite interesting; but for our purposes, all we need to know is that just two relative points are all that's technically needed to give our shape the possibility of all sorts of curves. This places the responsibility of defining curves in an object that would represent a point plus any handles (a more refined and domain specific view of a point). The repercussions of this are that we can think of lines and curves uniformly, and that we can think of a shape as an ordered collection of points (meaning our point-plus-handles definition of points).
We can simplify things down to points rather than curves or lines because the points lead the path along which the line will be drawn—only when we draw do we need to connect the points with curved and straight lines. The benefits of viewing a shape as an ordered collection of points might not be as clear as the problems created by trying to describe a shape as lines and curves. If a vector graphic were composed of lines, then it would be impossible to modify one line's ending point without modifying another line's beginning point. To make sure the lines stay synchronized you would likely end up coming right back to a view of the shape as an ordered collection of points.
Fill and Stroke
Vector graphics conventionally allow the modification of fill and stroke styles. Think of the stroke as the pen you use (and the way you use it) to draw the lines of a rectangle and fill as the color, pattern or texture with which you might fill the center of the rectangle. Stroke and fill will be covered in much more detail later in this course, for now, know that we'll need them in the long term in our system. For the moment, we'll focus on the geometrical side of things and ignore how things actually get drawn, which leaves the stroke and fill out of our scope.
Closed and Open Shapes
One thing that may not be apparent already is that there can be both closed and open shapes. Although we could simulate closed shapes by having an extra point—such as a rectangle with five points, the last and first both being the origin—this would not be conceptually (or geometrically) accurate.
The rectangle example should sufficiently show why using another point is unnatural. Instead, we can use the concept of "closed" (and its converse, "open"), to describe some of the differences between a rectangle and a line that travels across four points. For the implementation, the difference will be trivial: closed draws an extra line from the last point to the first. The distinction between closed and open shapes is not a profound feature but merely a requirement.
Creating the VectorGraphic Class
In this lesson, we will ignore the actual drawing of vector graphics onto unstructured surfaces. This leaves us with a VectorGraphic class that will be fairly minimal, but even with its simple requirements, the VectorGraphic class still requires a good deal of thought (or experience on which to draw intuitions) to be implemented in C++. Some questions, like what standard collection implementation (if any) should be used, might already be surfacing in your mind. Others, like whether the point should be a reference or value object, might not.
Points: Reference or Value Objects?
Reference or value is a distinction that becomes an issue in C++, but not in most other object-oriented languages. In C++, value objects are generally simple, concrete classes that are used like primitive types (such as int, char, and float) and are often copied. A reference object, on the other hand, is always used by reference and never copied. C++ also allows all the gradients in between reference and value. In C++, an inexperienced developer might define a class that uses polymorphic methods but also overloads operators. This would be a serious error, since the overloaded operators cannot be polymorphic and thus, when the class in question is subclassed, the operators will work incorrectly. An example is in order.
Example 1.1 might be a bit obfuscated, but the fact that it is possible is a testament to the complexity (otherwise known as freedom) of C++. To keep problems like the code in our example from arising, it's best to keep value objects extremely simple. Use them only when they will behave exactly like primitive types, including support of most standard operators (especially assignment), and do not attempt to mix paradigms by using inheritance or polymorphism in any way.
By default, you should assume reference semantics. Most non-trivial objects will be reference objects, and many of the trivial ones will, over the life of a system, begin to require a more sophisticated approach that merits making them reference objects. Value objects are exceptions, not rules. A simple motive for preferring reference objects is that a value object provides no features that a reference object can't provide. You might lose the ability to make your object look like a primitive type, but this is merely a loss of syntactical (and subjective) convenience.
Important
A class cannot preclude itself from being used as a reference object, but it can preclude itself from being used as a value object.
One more important property to remember when distinguishing value objects from reference objects is that the class cannot preclude itself from being used as a reference object, but it can preclude itself from being used as a value object. This is why we use the term "object" instead of "class" when speaking of their differences—the class itself is not always definitively going to only allow its instances to be used one way or the other. Regardless of whether you can restrict usage of your class, you can always make your intentions clear via interface and documentation.
Back to our vector graphics system. The Point class could potentially be designed for instantiation as a value object, because its history is predictable and simple. It needn't be anything more than setters and getters to two attributes: an x coordinate and a y coordinate. Our choice of whether to design it with the express intention of reference semantics or not would be based only on what's more consistent or expected, not on what features we might need (or at least, that's how it seems at present).
Supporting Closed and Open Shapes
As discussed in our analysis, open and closed shapes must both be supported. Clearly, this can be implemented via an attribute (whether a boolean or an enumerated type), but what methods will the client call to open and close the shape? Although your first instinct might be to create accessors, this might not work well for open and close. To open the shape should you setOpen (true) or setClosed (false)? The first hint that both of these would be less than ideal for method names is that they don't have normal naming semantics: they aren't normal verb phrases. More specifically, open and closed are states of being, not fields, so to speak. Naming in this way will also reveal an implementation detail—your true requirement is simply to allow open and close requests, but by using accessors for a boolean you reveal that your implementation does this via a boolean attribute. Other implementations might wish to support opening and closing in some other way, so making this implementation detail public will be making the other implementations more awkward.
For these reasons, separate open and close methods might make more sense. Of course, the terms open and close are heavily overloaded, and some extra distinction like openShape and closeShape might be in order. With all of this discussion for a seemingly simple task of naming the method or methods that allow opening and closing of a shape, we're still not finished, because openShape and closeShape will give clients the impression that they should pass in a shape object as a single parameter (just like registerStudent implies at least a single parameter of a Student object to most developers). For now, we'll ignore this problem and move on.
It may also be necessary for clients to determine whether a shape is open or closed. Here we can provide an isOpen method, an isClosed method or both. Although providing both might cause confusion, it also will make algorithms cleaner and more self-documenting, as vectorGraphic.isClosed () is more clear than !vectorGraphic.isOpen ().
Reporting Bounds
Reporting bounds will be a requirement for most non-trivial usages of vector graphics. A reality of many technologies is that they work with an idealized horizontally-aligned rectangular form for bounds. In other words, although bounds could be described quite simply as the shape itself, or perhaps as an arbitrarily-aligned rectangular approximation of the shape, they are instead described basically as a width and height.
Key Terms
horizontal alignment
We will revisit the idea of rectangular bounds and horizontal alignment in a future lesson when we look at skewing and scaling. But the term horizontal alignment might need some explanation. "Horizontal alignment" refers to the archetypal rectangle whose sides all have no slope or zero slope. A rectangle such as the one in figure 1.4-A is what most systems call a rectangle.
Figure 1.4 A—Archetypal Rectangle
The rectangle in figure 1.4 A can be described by a single upper left and lower right coordinate, or, if its points are relative to its own upper left, simply a lower right coordinate—or width and height. Be aware, though, that this is not the true-and-proper definition of a rectangle and that the rectangle shown in figure 1.4-B is just as much a rectangle as the archetypal one above.
Figure 1.4-B—Less Archetypal Rectangle
For this reason, we'll simplify our vector graphic and just report width and height—which comprises the essence of what most of our clients are looking for. For example, if they are allocating memory for drawing, they'll need to multiply the width times the height to create a buffer. Once they do this they can draw the vector graphic into that buffer. In this case, they also won't need an upper left and lower right, so much as simply the relative lower right (or, once again, width and height).
Choosing a Collection
Choosing the best collection for a particular application in C++ requires some basic knowledge data structures and algorithms. In Lesson Two, we'll look deeper into the decision-making process; but for now we'll keep things simple and just use an std::vector<> for our points.
Summary of Choices
Applying all of our decisions, our basic knowledge of C++ collections and self-documenting naming practices, we now have a VectorGraphic that looks like example 1.3.
In your assignment for this lesson, you will complete and test this implementation of VectorGraphic via loading and saving an XML-like definition of a VectorGraphic.
New in C++ 11
lvalues, rvalues, and universal references
lvalues
The fundamental objects in C++ that we're used to dealing with have “identity.” This means they occupy a specific address in memory that we can access, and we can use their address to distinguish one particular object from another when they might otherwise be identical (have the same value). These types of objects are called “lvalues”, because they can appear on the left-hand side of an assignment.
rvalues
C++ (and its ancestor C) have always had other types of objects with no “identity”. Typically these are temporary objects created by the compiler; things like the return value of a function, or a temporary copy of an object used as a function argument. We typically don’t know or care about the actual address of a temporary, and even if we did it’s not safe to store that address for use later on. These types of values are called “rvalues”, to contrast with the term “lvalue”. An rvalue is something you cannot assign to.
C++11 leverages this concept of rvalues, and provides new syntax for us to work with them. The notation “&&” means “rvalue reference”: a reference to which we can bind an rvalue. C++11 takes advantage of the fact that an rvalue reference is a reference to something that nobody can assign to. That means its safe to “steal” its value… to move it somewhere else. Moving a value is typically cheaper than copying, usually avoiding the cost of constructing a new object to avoid compromising the original source object. This is referred to as “move semantics”. More about this later.
universal references
Actually, when you see the notation "&&", it doesn't *always* mean you're looking at an rvalue reference - it depends on the context. Sometimes "T&&" means "rvalue reference", but sometimes it means "either rvalue reference or lvalue reference" - what Meyers calls a "universal reference". It can bind to anything: rvalue or lvalue, const or non-const, volatile or non-volatile.
There are two contexts in which T&& means "universal reference". The most common is function template parameters, like this:
The second context is in auto declarations, like this:
(Examples taken directly from Scott Meyers' "Effective Modern C++".)
In both of these contexts, the compiler must deduce the type. If type deduction results in T being an rvalue, T&& is an rvalue reference; if type deduction yields T as an lvalue, T&& is an lvalue reference. Continuing with Scott Meyers' example:
Function parameters vs. arguments
In C++11 it’s useful to emphasize the distinction between a function’s formal arguments — typically called “parameters” — and its actual arguments — usually called simply “arguments”.
Function parameters are the variables you have inside the body of a function. Function arguments are the expressions evaluated at the location where the function is called.
The distinction is important because function parameters are always lvalues — even though they may be declared in the function signature with “&&” (meaning its type is an rvalue reference). The arguments with which the function parameters are initialized may be lvalues or rvalues — impacting whether values are moved or copied into the parameters.
Code Example 1.1 class Animal { public: virtual int getAge () const; virtual std::string const& getSpeciesName() const; bool operator==(Animal const& other) const { return (other.getAge() == getAge ()) && (other.getSpeciesName () == getSpeciesName)); } }; class Mammal : public Animal { private: typedef Animal Super; public: enum Gender { Male, Female }; virtual Gender getGender () const; bool operator== (Mammal const& other) const { return Super::operator==(other) && other.getGender() == getGender()); } }; // ... Animal* femaleCat = new FemaleCat(); Animal* maleCat = new MaleCat (); if (*femaleCat == *maleCat) { // this will be true when it should be false. If we had created // an isEqualTo polymorphic method, we wouldn't have this particular problem. throw WeHaveASeriousProblemHere(); }
Explanation / Answer
CURVES
#include<iostream.h>
#include<graphics.h>
#include<conio.h>
#include<stdio.h>
struct point
{
int x,y;
};
void hermite(point p1,point p4,double r1,double r4)
{
float x,y,t;
for(t=0.0;t<=1.0;t+=.001)
{
x=(2*t*t*t-3*t*t+1)*p1.x+(-2*t*t*t+3*t*t)*p4.x+(t*t*t-2*t*t+t)*r1+(t*t*t-t*t)*r4;
y=(2*t*t*t-3*t*t+1)*p1.y+(-2*t*t*t+3*t*t)*p4.y+(t*t*t-2*t*t+1)*r1+(t*t*t-t*t)*r4;
putpixel(x,y,YELLOW);
}
}
int main()
{
int gd=DETECT,gm;
double r1,r4;
initgraph(&gd,&gm,"..//BGI");
point p1,p2;
printf("Enter 2 hermite points: ");
scanf("%d%d%d%d",&p1.x,&p1.y,&p2.x,&p2.y);
printf("Enter the tangents at p1,p4");
scanf("%d%d",&r1,&r4);
cleardevice();
hermite(p1,p2,r1,r4);
putpixel(x1,y1,WHITE);
putpixel(x2,y2,WHITE);
getch();
closegraph();
return 0;
}
LINES
#include
#include
int line1(int,int,int,int);
void hermite(int x1,int y1,int x2,int y2,float slope1,float slope2)
{
float x,y;
float u;
for(u=0.00;u<=1.0;u+=0.001)
{
x=(2*u*u*u-3*u*u+1)*x1+(-2*u*u*u+3*u*u)*x2+(u*u*u-2*u*u+u)*slope1*x1+(u*u*u-u*u)*slope2*x2;
y=(2*u*u*u-3*u*u+1)*y1+(-2*u*u*u+3*u*u)*y2+(u*u*u-2*u*u+u)*slope1*x1+(u*u*u-u*u)*slope2*x2;
putpixel(20+(int)(x+0.5),240-(int)(y+0.5),RED);
}
}
int main()
{
int gd=DETECT,gm;
initgraph(&gd,&gm,"c:\tc\bgi");
int i,j,k,l,m,n,x,y,z;
//hermite(10,10,100,100,3,4);
hermite(100,50,100,100,0.9,-1);
line1(100,0,100,100);
line1(100,50,120,0);
line1(140,0,150,100);
line1(150,100,160,0);
line1(145,50,155,50);
line1(180,0,180,100);
line1(180,100,200,0);
line1(200,0,200,100);
line1(220,0,230,100);
line1(230,100,240,0);
line1(225,50,235,50);
getch();
closegraph();
return 0;
}
int line1(int x1,int y1,int x2,int y2)
{
setcolor(RED);
line(20+x1,240-y1,20+x2,240-y2);
return 0;
}
Point
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.