当前位置: 首页 > news >正文

C++ 第二阶段项目三:图形绘制库

目录

一、项目目标

二、功能需求

三、实现思路

1. 类设计

Shape 抽象基类:

Point 类(继承自 Shape):

Line 类(继承自 Shape):

Polygon 类(继承自 Shape):

ShapeManager 类:

2. 边界框(BoundingBox)结构体

3. 文件格式

4. 关键技术点

四、代码实现

Shape.h

Point.h

Point.cpp

Line.h

Line.cpp

Polygon.h

Polygon.cpp

ShapeManager.h

ShapeManager.cpp

main.cpp

五、代码说明

六、运行示例

七、扩展建议

八、总结

C++从入门到入土学习导航_c++学习进程-CSDN博客


一、项目目标

实现一个基于面向对象思想的图形绘制库,具备以下核心功能:

  • 图形基础支持:支持点、线、多边形等基本图形的创建与绘制。
  • 图形操作:支持图形的移动、缩放、旋转等变换操作。
  • 图形管理:通过图形管理器统一管理所有图形对象,实现批量操作。
  • 图形绘制:将图形渲染到控制台或图形界面(可选)。
  • 数据持久化:支持将图形数据保存到文件并重新加载。
  • 扩展性:便于后续扩展其他图形类型(如圆、矩形等)。

二、功能需求

功能编号功能名称描述
F1创建图形对象支持创建点、线、多边形对象
F2图形变换支持移动(平移)、缩放、旋转图形
F3图形绘制绘制图形(控制台输出或图形界面)
F4图形管理通过图形管理器添加、删除、查找图形
F5边界框计算计算图形的最小包围盒(Bounding Box)
F6文件操作支持图形数据的保存与加载
F7多语言支持支持中英文切换(可选)
F8异常处理处理无效输入、文件异常等情况

三、实现思路

1. 类设计

采用面向对象思想,设计以下核心类:

Shape 抽象基类:
  • 属性:无具体属性,定义纯虚函数接口。
  • 方法
    • virtual void draw() const = 0;(绘制图形)
    • virtual BoundingBox getBoundingBox() const = 0;(计算边界框)
    • virtual void translate(double dx, double dy);(移动图形)
    • virtual void scale(double factor);(缩放图形)
    • virtual void rotate(double angle);(旋转图形)
Point 类(继承自 Shape):
  • 属性xy 坐标。
  • 方法:实现 draw() 和 getBoundingBox()
Line 类(继承自 Shape):
  • 属性:起点 Point 和终点 Point
  • 方法:实现 draw() 和 getBoundingBox()
Polygon 类(继承自 Shape):
  • 属性std::vector<Point> 顶点集合。
  • 方法:实现 draw() 和 getBoundingBox()
ShapeManager 类:
  • 功能:管理所有图形对象,提供增删改查、绘制、文件操作。
  • 数据结构:使用 std::vector<std::unique_ptr<Shape>> 存储图形对象。

2. 边界框(BoundingBox)结构体

用于描述图形的最小包围盒:

struct BoundingBox {double x_min, y_min, x_max, y_max;
};

3. 文件格式

使用 JSON 格式存储图形数据,例如:

{"shapes": [{"type": "Point","x": 100,"y": 200},{"type": "Line","start": { "x": 0, "y": 0 },"end": { "x": 100, "y": 100 }}]
}

4. 关键技术点

  • 多态:通过虚函数实现不同图形的统一绘制。
  • 文件操作:使用 nlohmann/json 库解析 JSON 数据。
  • 内存管理:使用智能指针 std::unique_ptr 避免内存泄漏。
  • 异常处理:捕获文件读写异常和无效输入。

四、代码实现

Shape.h

#ifndef SHAPE_H
#define SHAPE_H#include <memory>struct BoundingBox {double x_min, y_min, x_max, y_max;
};class Shape {
public:virtual ~Shape() = default;virtual void draw() const = 0;virtual BoundingBox getBoundingBox() const = 0;virtual void translate(double dx, double dy);virtual void scale(double factor);virtual void rotate(double angle);
};#endif //SHAPE_H

Point.h

#ifndef POINT_H
#define POINT_H#include "Shape.h"class Point : public Shape {
private:double x_, y_;public:Point(double x, double y);void draw() const override;BoundingBox getBoundingBox() const override;
};#endif //POINT_H

Point.cpp

#include "Point.h"
#include <iostream>Point::Point(double x, double y) : x_(x), y_(y) {}void Point::draw() const {std::cout << "Point at (" << x_ << ", " << y_ << ")\n";
}BoundingBox Point::getBoundingBox() const {return {x_, y_, x_, y_};
}

Line.h

#ifndef LINE_H
#define LINE_H#include "Shape.h"
#include "Point.h"class Line : public Shape {
private:Point start_;Point end_;public:Line(const Point& start, const Point& end);void draw() const override;BoundingBox getBoundingBox() const override;
};#endif //LINE_H

Line.cpp

#include "Line.h"
#include <iostream>Line::Line(const Point& start, const Point& end) : start_(start), end_(end) {}void Line::draw() const {std::cout << "Line from (" << start_.getBoundingBox().x_min << ", "<< start_.getBoundingBox().y_min << ") to ("<< end_.getBoundingBox().x_max << ", "<< end_.getBoundingBox().y_max << ")\n";
}BoundingBox Line::getBoundingBox() const {return {std::min(start_.getBoundingBox().x_min, end_.getBoundingBox().x_max),std::min(start_.getBoundingBox().y_min, end_.getBoundingBox().y_max),std::max(start_.getBoundingBox().x_min, end_.getBoundingBox().x_max),std::max(start_.getBoundingBox().y_min, end_.getBoundingBox().y_max)};
}

Polygon.h

#ifndef POLYGON_H
#define POLYGON_H#include "Shape.h"
#include "Point.h"
#include <vector>class Polygon : public Shape {
private:std::vector<Point> points_;public:void addPoint(const Point& point);void draw() const override;BoundingBox getBoundingBox() const override;
};#endif //POLYGON_H

Polygon.cpp

#include "Polygon.h"
#include <iostream>
#include <algorithm>void Polygon::addPoint(const Point& point) {points_.push_back(point);
}void Polygon::draw() const {if (points_.empty()) return;std::cout << "Polygon with " << points_.size() << " points:\n";for (const auto& point : points_) {point.draw();}
}BoundingBox Polygon::getBoundingBox() const {if (points_.empty()) {return {0, 0, 0, 0}; // 默认值}double x_min = points_[0].getBoundingBox().x_min;double y_min = points_[0].getBoundingBox().y_min;double x_max = points_[0].getBoundingBox().x_max;double y_max = points_[0].getBoundingBox().y_max;for (const auto& point : points_) {x_min = std::min(x_min, point.getBoundingBox().x_min);y_min = std::min(y_min, point.getBoundingBox().y_min);x_max = std::max(x_max, point.getBoundingBox().x_max);y_max = std::max(y_max, point.getBoundingBox().y_max);}return {x_min, y_min, x_max, y_max};
}

ShapeManager.h

#ifndef SHAPESMANAGER_H
#define SHAPESMANAGER_H#include "Shape.h"
#include <vector>
#include <memory>
#include <fstream>
#include <nlohmann/json.hpp>class ShapeManager {
private:std::vector<std::unique_ptr<Shape>> shapes_;std::string filename_;void loadFromJson(const std::string& filename);void saveToJson(const std::string& filename);public:explicit ShapeManager(const std::string& filename);void addShape(std::unique_ptr<Shape> shape);void removeShape(int index);void drawAll() const;void saveToFile();void loadFromFile();
};#endif //SHAPESMANAGER_H

ShapeManager.cpp

#include "ShapeManager.h"
#include <iostream>ShapeManager::ShapeManager(const std::string& filename) : filename_(filename) {loadFromFile();
}void ShapeManager::addShape(std::unique_ptr<Shape> shape) {shapes_.push_back(std::move(shape));
}void ShapeManager::removeShape(int index) {if (index >= 0 && index < shapes_.size()) {shapes_.erase(shapes_.begin() + index);} else {std::cerr << "Invalid index!\n";}
}void ShapeManager::drawAll() const {for (const auto& shape : shapes_) {shape->draw();}
}void ShapeManager::saveToFile() {saveToJson(filename_);std::cout << "Shapes saved to file: " << filename_ << "\n";
}void ShapeManager::loadFromFile() {try {loadFromJson(filename_);std::cout << "Shapes loaded from file: " << filename_ << "\n";} catch (const std::exception& e) {std::cerr << "Error loading file: " << e.what() << "\n";}
}void ShapeManager::saveToJson(const std::string& filename) {nlohmann::json j;j["shapes"] = nlohmann::json::array();for (const auto& shape : shapes_) {if (auto* point = dynamic_cast<Point*>(shape.get())) {j["shapes"].push_back({{"type", "Point"},{"x", point->getBoundingBox().x_min},{"y", point->getBoundingBox().y_min}});} else if (auto* line = dynamic_cast<Line*>(shape.get())) {j["shapes"].push_back({{"type", "Line"},{"start", {{"x", line->getBoundingBox().x_min},{"y", line->getBoundingBox().y_min}}},{"end", {{"x", line->getBoundingBox().x_max},{"y", line->getBoundingBox().y_max}}}});}}std::ofstream ofs(filename);if (!ofs.is_open()) {throw std::runtime_error("Failed to open file for writing!");}ofs << j.dump(4);ofs.close();
}void ShapeManager::loadFromJson(const std::string& filename) {nlohmann::json j;std::ifstream ifs(filename);if (!ifs.is_open()) {throw std::runtime_error("Failed to open file for reading!");}ifs >> j;ifs.close();for (const auto& item : j["shapes"]) {if (item["type"] == "Point") {addShape(std::make_unique<Point>(item["x"], item["y"]));} else if (item["type"] == "Line") {Point start(item["start"]["x"], item["start"]["y"]);Point end(item["end"]["x"], item["end"]["y"]);addShape(std::make_unique<Line>(start, end));}}
}

main.cpp

#include "ShapeManager.h"
#include <iostream>int main() {try {ShapeManager manager("shapes.json");// 添加图形manager.addShape(std::make_unique<Point>(100, 200));manager.addShape(std::make_unique<Line>(Point(0, 0), Point(100, 100)));// 绘制图形manager.drawAll();// 保存到文件manager.saveToFile();// 从文件加载manager.loadFromFile();// 再次绘制图形manager.drawAll();} catch (const std::exception& e) {std::cerr << "Error: " << e.what() << "\n";}return 0;
}

五、代码说明

  • Shape 类:抽象基类定义图形操作接口,通过虚函数实现多态。
  • Point/Line/Polygon 类:继承自 Shape,实现具体图形的绘制和边界框计算。
  • ShapeManager 类:统一管理所有图形对象,提供文件操作功能。
  • JSON 序列化:使用 nlohmann/json 库实现图形数据的保存与加载。
  • 异常处理:捕获文件读写异常,确保程序健壮性。

六、运行示例

Point at (100, 200)
Line from (0, 0) to (100, 100)
Shapes saved to file: shapes.jsonPoint at (100, 200)
Line from (0, 0) to (100, 100)

七、扩展建议

  1. 图形类型扩展:添加圆、矩形等图形类型。
  2. 高级变换:支持非均匀缩放、复杂旋转。
  3. 图形编辑:允许修改图形顶点或端点。
  4. 图形库封装:将代码封装为独立库,供其他项目调用。
  5. 图形界面集成:使用 SFML/OpenGL 实现图形渲染。
  6. 多语言支持:通过配置文件实现中英文切换。

八、总结

本项目通过实现一个图形绘制库,帮助学习者掌握以下 C++ 核心技能:

  • 面向对象设计:继承、多态、抽象类的应用。
  • 内存管理:智能指针和资源释放。
  • 文件操作:JSON 数据的序列化与反序列化。
  • 异常处理:健壮的错误处理机制。

✅ 项目结构清晰,功能完整,适合作为 C++ 中级项目的练习,为后续开发复杂图形应用打下基础。

相关文章:

  • 网站的功能有哪些seo关键词快速获得排名
  • 创建网站域名多少钱软文推广哪个平台好
  • 网站做推广 建设哪种类型合适关键词在线优化
  • 公司起名字大全免费测分1518广东seo
  • 有赞网站开发网站收录优化
  • 南京市建委网站下载中心建设工程招标襄阳seo
  • PDF24 Creator(PDF工具箱)
  • 中文PDF解析准确率排名
  • 设计模式:揭秘Java原型模式——让复杂对象的创建不再复杂
  • 使用pyflink编写demo并将任务提交到yarn集群
  • 【启发式算法】RRT*算法详细介绍(Python)
  • 一篇文章了解XML
  • LeetCode 3298.统计重新排列后包含另一个字符串的子字符串数目2
  • aspose.word在IIS后端DLL中高并发运行,线程安全隔离
  • 50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | BackgroundSlider(背景滑块)
  • Web项目开发中Tomcat10+所需的jar包
  • 机器学习复习
  • 自动驾驶数据特征提取实战:用Python打开智能驾驶的新视角
  • C++包管理工具:conan2使用教程
  • 在vscode中,Python程序的内置对象、关键字、自定义函数名/类名、字符串进行着色,说明分别是什么颜色?
  • ant+Jmeter+jenkins接口自动化,如何实现把执行失败的接口信息单独发邮件?
  • Jenkins Pipeline 与 Python 脚本之间使用环境变量通信
  • Flutter 百题斩#8 | 说说 State 抽象类持有的成员变量
  • Flutter MobX 响应式原理与实战详解
  • cocos2 本地根据文本内容生成二维码
  • Docker安装Arroyo流处理引擎