diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 8a09551..5ac5475 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -43,16 +43,10 @@ jobs: with: submodules: true - - name: Set up QEMU - if: runner.os == 'Linux' - uses: docker/setup-qemu-action@v2 - with: - platforms: all - - uses: pypa/cibuildwheel@v2.12.0 env: # CIBW_ARCHS: auto64 - CIBW_ARCHS_LINUX: x86_64 aarch64 + CIBW_ARCHS_LINUX: x86_64 CIBW_ARCHS_WINDOWS: AMD64 # ARM64 CIBW_ARCHS_MACOS: x86_64 arm64 CIBW_BEFORE_BUILD: pip install numpy fire --prefer-binary diff --git a/.gitmodules b/.gitmodules index ced1d11..8197afb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,6 @@ path = pybind11 url = https://github.com/pybind/pybind11.git branch = master +[submodule "headers"] + path = headers + url = https://github.com/cubao/headers.git diff --git a/CMakeLists.txt b/CMakeLists.txt index b7804d3..efe9c3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,14 @@ cmake_minimum_required(VERSION 3.4...3.18) -project(cubao_cmake_example) +project(naive_svg) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +include_directories(headers/include) add_subdirectory(pybind11) -pybind11_add_module(pybind11_cubao_cmake_example src/main.cpp) +file(GLOB SRCS src/*.cpp) +pybind11_add_module(_naive_svg ${SRCS}) # EXAMPLE_VERSION_INFO is defined by setup.py and passed into the C++ code as a # define (VERSION_INFO) here. -target_compile_definitions(pybind11_cubao_cmake_example +target_compile_definitions(_naive_svg PRIVATE VERSION_INFO=${EXAMPLE_VERSION_INFO}) diff --git a/Makefile b/Makefile index 7ca3ff3..dcd5a6f 100644 --- a/Makefile +++ b/Makefile @@ -55,11 +55,6 @@ python_build: python_sdist: $(PYTHON) setup.py sdist python_test: - $(PYTHON) -c 'from pybind11_rdp import rdp; print(rdp([[1, 1], [2, 2], [3, 3], [4, 4]]))' - $(PYTHON) -c 'import cubao_cmake_example; print(cubao_cmake_example.add(1, 2))' - $(PYTHON) -m cubao_cmake_example add 1 2 - $(PYTHON) -m cubao_cmake_example subtract 9 4 - $(PYTHON) -m cubao_cmake_example pure_python_func --arg1=43234 python3 -m pip install pytest pytest tests diff --git a/README.md b/README.md index 8c189ec..8fc8c15 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# cmake-example +# naive-svg -Online document: **[readthedocs](http://cmake-example.readthedocs.io/)** +Online document: **[readthedocs](http://naive-svg.readthedocs.io/)** @@ -9,207 +9,5 @@ Online document: **[readthedocs](http://cmake-example.readthedocs.io/)** Install: ```bash -python3 -m pip install cubao_cmake_example # install from pypi -python3 -c 'import cubao_cmake_example; print(cubao_cmake_example.add(1, 2))' +python3 -m pip install naive-svg # install from pypi ``` - -CLI interface: (created with [python-fire](https://github.com/google/python-fire)) - -```bash -python3 -m cubao_cmake_example add 1 2 -python3 -m cubao_cmake_example subtract 9 4 -``` - -Help: - -```bash -$ python3 -m cubao_cmake_example --help -INFO: Showing help with the command '__main__.py -- --help'. - -NAME - __main__.py - -SYNOPSIS - __main__.py GROUP | COMMAND - -GROUPS - GROUP is one of the following: - - fire - The Python Fire module. - -COMMANDS - COMMAND is one of the following: - - add - add(arg0: int, arg1: int) -> int - - subtract - subtract(arg0: int, arg1: int) -> int - - pure_python_func -``` - -```bash -$ python3 -m cubao_cmake_example pure_python_func --help -INFO: Showing help with the command '__main__.py pure_python_func -- --help'. - -NAME - __main__.py pure_python_func - -SYNOPSIS - __main__.py pure_python_func - -FLAGS - --arg1=ARG1 - Type: int - Default: 42 - --arg2=ARG2 - Type: float - Default: 3.14 - --arg3=ARG3 - Type: str - Default: 'you shall not pass' - -$ python3 -m cubao_cmake_example pure_python_func --arg1=43234 -int: 43234, float: 3.14, str: you shall not pass -``` - - - ---- - -# Make a release - -(We now use Github Workflow to release to pypi. Skip the rest if you don't want to manually compile wheels.) - -## On linux - -Install docker then - -``` -make python_build_all_in_linux -make upload_wheels -``` - -## On macOS - -Install c++ compiler and cmake. - -Install conda and envs: - -``` -# conda create -y -n py36 python=3.6 -# conda create -y -n py37 python=3.7 -conda create -y -n py38 python=3.8 -conda create -y -n py39 python=3.9 -conda create -y -n py310 python=3.10 -conda env list -``` - -Then - -``` -make python_build_all_in_macos -make upload_wheels -``` - -## On windows - -Install visual studio and cmake, (also git for windows, maybe). - -Install conda and envs same as on macOS, then: - -``` -make python_build_all_in_windows -make upload_wheels -``` - ---- - -# cmake_example for pybind11 - -[![Gitter][gitter-badge]][gitter-link] - -| CI | status | -|----------------------|--------| -| MSVC 2015 | [![AppVeyor][appveyor-badge]][appveyor-link] | -| conda.recipe | [![Conda Actions Status][actions-conda-badge]][actions-conda-link] | -| pip builds | [![Pip Actions Status][actions-pip-badge]][actions-pip-link] | -| [`cibuildwheel`][] | [![Wheels Actions Status][actions-wheels-badge]][actions-wheels-link] | - -[gitter-badge]: https://badges.gitter.im/pybind/Lobby.svg -[gitter-link]: https://gitter.im/pybind/Lobby -[actions-badge]: https://github.com/pybind/cmake_example/workflows/Tests/badge.svg -[actions-conda-link]: https://github.com/pybind/cmake_example/actions?query=workflow%3A%22Conda -[actions-conda-badge]: https://github.com/pybind/cmake_example/workflows/Conda/badge.svg -[actions-pip-link]: https://github.com/pybind/cmake_example/actions?query=workflow%3A%22Pip -[actions-pip-badge]: https://github.com/pybind/cmake_example/workflows/Pip/badge.svg -[actions-wheels-link]: https://github.com/pybind/cmake_example/actions?query=workflow%3AWheels -[actions-wheels-badge]: https://github.com/pybind/cmake_example/workflows/Wheels/badge.svg -[appveyor-link]: https://ci.appveyor.com/project/dean0x7d/cmake-example/branch/master -[appveyor-badge]: https://ci.appveyor.com/api/projects/status/57nnxfm4subeug43/branch/master?svg=true - -An example [pybind11](https://github.com/pybind/pybind11) module built with a -CMake-based build system. This is useful for C++ codebases that have an -existing CMake project structure. This is in many cases superseded by -[`scikit_build_example`](https://github.com/pybind/scikit_build_example), which uses -[scikit-build][], a tool from the makers of CMake designed to allow Python -packages to be driven from CMake. However, there are still cases where you -might want full control over the CMake run; and both of these approaches have -some trade-offs not present in a pure setuptools build (see -[`python_example`](https://github.com/pybind/python_example)). Python 3.6+ required; -see the commit history for older versions of Python. - -## Prerequisites - -* A compiler with C++11 support -* Pip 10+ or CMake >= 3.4 (or 3.8+ on Windows, which was the first version to support VS 2015) -* Ninja or Pip 10+ - - -## Installation - -Just clone this repository and pip install. Note the `--recursive` option which is -needed for the pybind11 submodule: - -```bash -git clone --recursive https://github.com/pybind/cmake_example.git -pip install ./cmake_example -``` - -With the `setup.py` file included in this example, the `pip install` command will -invoke CMake and build the pybind11 module as specified in `CMakeLists.txt`. - - - -## Building the documentation - -Documentation for the example project is generated using Sphinx. Sphinx has the -ability to automatically inspect the signatures and documentation strings in -the extension module to generate beautiful documentation in a variety formats. -The following command generates HTML-based reference documentation; for other -formats please refer to the Sphinx manual: - - - `cd cmake_example/docs` - - `make html` - - -## License - -Pybind11 is provided under a BSD-style license that can be found in the LICENSE -file. By using, distributing, or contributing to this project, you agree to the -terms and conditions of this license. - - -## Test call - -```python -import cubao_cmake_example -cubao_cmake_example.add(1, 2) -``` - -[`cibuildwheel`]: https://cibuildwheel.readthedocs.io -[FAQ]: http://pybind11.rtfd.io/en/latest/faq.html#working-with-ancient-visual-studio-2009-builds-on-windows -[vs2015_runtime]: https://www.microsoft.com/en-us/download/details.aspx?id=48145 -[scikit-build]: https://scikit-build.readthedocs.io/en/latest/ diff --git a/cubao_cmake_example/__init__.py b/cubao_cmake_example/__init__.py deleted file mode 100644 index 87bbf8a..0000000 --- a/cubao_cmake_example/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from pybind11_cubao_cmake_example import * # noqa: F403 -from pybind11_cubao_cmake_example import __version__ # noqa: F401 - - -def pure_python_func( - arg1: int = 42, - arg2: float = 3.14, - arg3: str = "you shall not pass", -) -> str: - return f"int: {arg1}, float: {arg2}, str: {arg3}" diff --git a/docs/about/license.md b/docs/about/license.md index c22fa08..ce2164e 100644 --- a/docs/about/license.md +++ b/docs/about/license.md @@ -7,8 +7,7 @@ The legal stuff. ## Included projects * [pybind/pybind11](https://github.com/pybind/pybind11/blob/master/LICENSE) (BSD) -* [pybind/cmake_example](https://github.com/pybind/cmake_example/blob/master/LICENSE) (BSD) ## License (BSD) -See . +See . diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index e5bda31..5a6f0a2 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -4,27 +4,19 @@ ## Upgrading -To upgrade `cubao_cmake_example` to the latest version, use pip: +To upgrade `naive-svg` to the latest version, use pip: ```bash -pip install -U cubao_cmake_example +pip install -U naive-svg ``` -## Version 0.0.3 (2023-03-04) +## Version 0.0.1 (2023-09-02) -* Add macOS arm64 (for m1 chip) - -## Version 0.0.2 (2023-03-03) - -* Release to pypi on GitHub workflow - -## Version 0.0.1 (2022-10-14) - -* First release to pypi +* TODO --- You can also checkout releases on: -- GitHub: -- PyPi: +- GitHub: +- PyPi: diff --git a/docs/code.md b/docs/code.md index c8a0bec..560dbde 100644 --- a/docs/code.md +++ b/docs/code.md @@ -1,44 +1,3 @@ # code -GitHub: [cmake-example](https://github.com/cubao/cmake_example) - -## CMakeLists.txt - -```cmake -{% - include-markdown "../CMakeLists.txt" - comments=false -%} -``` - -## src/main.cpp - -```cpp -{% - include-markdown "../src/main.cpp" - comments=false -%} -``` - -## setup.py - -
- code - -```python -{% - include-markdown "../setup.py" - comments=false -%} -``` - -
- -## tests/test_basic.py - -```cpp -{% - include-markdown "../tests/test_basic.py" - comments=false -%} -``` +GitHub: [cmake-example](https://github.com/cubao/pybind11-naive-svg) diff --git a/docs/index.md b/docs/index.md index fe2d7ce..c8bc98e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,13 +1,6 @@ -# cmake_example +# naive-svg -Example pybind11 module built with a CMake-based build system. - -It's a [fork](https://github.com/cubao/cmake_example) of [pybind/cmake_example](https://github.com/pybind/cmake_example) -with some modifications: - -- integrate python code along side c++ pybind code -- release to pypi -- use markdown document +TODO {% include-markdown "../README.md" @@ -16,8 +9,8 @@ with some modifications: %} ## Related diff --git a/headers b/headers new file mode 160000 index 0000000..805b524 --- /dev/null +++ b/headers @@ -0,0 +1 @@ +Subproject commit 805b524dfca4440e2980384ddbb9c1d6498909f6 diff --git a/mkdocs.yml b/mkdocs.yml index 3e7e733..f8fd99b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,9 +1,9 @@ -site_name: cmake-example -site_url: https://cmake-example.readthedocs.io -site_description: Example pybind11 module built with a CMake-based build system. +site_name: naive-svg +site_url: https://naive-svg.readthedocs.io +site_description: naive svg writer site_author: district10 -repo_url: https://github.com/cubao/cmake_example +repo_url: https://github.com/cubao/pybind11-naive-svg edit_uri: blob/master/docs/ theme: material diff --git a/naive_svg/__init__.py b/naive_svg/__init__.py new file mode 100644 index 0000000..fc644f6 --- /dev/null +++ b/naive_svg/__init__.py @@ -0,0 +1,2 @@ +from _naive_svg import * # noqa: F403 +from _naive_svg import __version__ # noqa: F401 diff --git a/cubao_cmake_example/__main__.py b/naive_svg/__main__.py similarity index 69% rename from cubao_cmake_example/__main__.py rename to naive_svg/__main__.py index 5f89a74..bb7b02d 100644 --- a/cubao_cmake_example/__main__.py +++ b/naive_svg/__main__.py @@ -1,4 +1,6 @@ -from cubao_cmake_example import * # noqa: F403 +from naive_svg import * # noqa: F403 + +# TODO, same sample code if __name__ == "__main__": import fire diff --git a/setup.py b/setup.py index 68bce96..71f8fd2 100644 --- a/setup.py +++ b/setup.py @@ -126,19 +126,19 @@ def build_extension(self, ext: CMakeExtension) -> None: # The information here can also be placed in setup.cfg - better separation of # logic and declaration, and simpler if you include description/version in a file. setup( - name="cubao_cmake_example", - version="0.0.3", + name="naive_svg", + version="0.0.1", author="tzx", author_email="dvorak4tzx@gmail.com", - url="https://cmake-example.readthedocs.io", - description="A test project using pybind11 and CMake", + url="https://naive-svg.readthedocs.io", + description="naive svg writer", long_description=open("README.md", encoding="utf-8").read(), long_description_content_type="text/markdown", packages=find_packages(), - ext_modules=[CMakeExtension("cubao_cmake_example")], + ext_modules=[CMakeExtension("_naive_svg")], cmdclass={"build_ext": CMakeBuild}, zip_safe=False, python_requires=">=3.7", - install_requires=["fire"], + install_requires=["fire", "numpy"], extras_require={"test": ["pytest>=6.0"]}, ) diff --git a/src/cubao_inline.hpp b/src/cubao_inline.hpp new file mode 100644 index 0000000..123599a --- /dev/null +++ b/src/cubao_inline.hpp @@ -0,0 +1,2 @@ +#pragma once +#define CUBAO_INLINE diff --git a/src/main.cpp b/src/main.cpp index 60309f3..4c73a9e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,33 +7,20 @@ int add(int i, int j) { return i + j; } namespace py = pybind11; -PYBIND11_MODULE(pybind11_cubao_cmake_example, m) +namespace cubao { - m.doc() = R"pbdoc( - Pybind11 example plugin - ----------------------- - - .. currentmodule:: cmake_example - - .. autosummary:: - :toctree: _generate - - add - subtract - )pbdoc"; +void bind_naive_svg(py::module &m); +} +PYBIND11_MODULE(_naive_svg, m) +{ m.def("add", &add, R"pbdoc( Add two numbers Some other explanation about the add function. )pbdoc"); - m.def( - "subtract", [](int i, int j) { return i - j; }, R"pbdoc( - Subtract two numbers - - Some other explanation about the subtract function. - )pbdoc"); + cubao::bind_naive_svg(m); #ifdef VERSION_INFO m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); diff --git a/src/naive_svg.hpp b/src/naive_svg.hpp new file mode 100644 index 0000000..ba8e900 --- /dev/null +++ b/src/naive_svg.hpp @@ -0,0 +1,538 @@ +#ifndef CUBAO_NAIVE_SVG_HPP +#define CUBAO_NAIVE_SVG_HPP + +// upstream +// https://github.com/cubao/headers/tree/main/include/cubao/naive_svg.hpp +// migrated from https://github.com/cubao/naive-svg/blob/master/svg.hpp + +#include +#include +#include + +#include +#include +#include + +namespace cubao +{ +// https://en.wikipedia.org/wiki/Fluent_interface +// https://rapidjson.org/md_doc_tutorial.html +// https://www.tutorialspoint.com/what-is-function-chaining-in-javascript +#ifndef SETUP_FLUENT_API +#define SETUP_FLUENT_API(Klass, VarType, VarName) \ + Klass &VarName(const VarType &v) \ + { \ + VarName##_ = v; \ + return *this; \ + } \ + VarType &VarName() { return VarName##_; } \ + const VarType &VarName() const { return VarName##_; } +#endif + +#ifndef SETUP_FLUENT_API_FOR_SVG_ELEMENT +#define SETUP_FLUENT_API_FOR_SVG_ELEMENT(KlassType) \ + SETUP_FLUENT_API(KlassType, Color, stroke) \ + SETUP_FLUENT_API(KlassType, Color, fill) \ + SETUP_FLUENT_API(KlassType, double, stroke_width) +#endif + +struct SVG +{ + using PointType = std::array; + enum COLOR + { + RED = 0xFF0000, + GREEN = 0x00FF00, // not css green + BLUE = 0x0000FF, + YELLOW = 0xFFFF00, + WHITE = 0xFFFFFF, + GRAY = 0x9B9B9B, + BLACK = 0x000000, + NONE = -1, + }; + + struct Color + { + Color(int rgb = -1 /* NONE */) + { + if (rgb >= 0) { + r_ = (rgb >> 16) & 0xFF; + g_ = (rgb >> 8) & 0xFF; + b_ = rgb & 0xFF; + } + } + Color(int r, int g, int b, float a = -1.f) : r_(r), g_(g), b_(b), a_(a) + { + } + SETUP_FLUENT_API(Color, int, r) + SETUP_FLUENT_API(Color, int, g) + SETUP_FLUENT_API(Color, int, b) + SETUP_FLUENT_API(Color, float, a) + bool invalid() const + { + return r_ < 0 || g_ < 0 || b_ < 0 || // + r_ > 255 || g_ > 255 || b_ > 255; + } + + friend std::ostream &operator<<(std::ostream &out, const SVG::Color &c); + void write(std::ostream &out) const + { + if (invalid()) { + out << "none"; + return; + } + if (0.f <= a_ && a_ <= 1.f) { + out << "rgba(" << r_ << "," << g_ << "," << b_ << "," << a_ + << ")"; + } else { + out << "rgb(" << r_ << "," << g_ << "," << b_ << ")"; + } + } + std::string to_string() const + { + std::stringstream ss; + write(ss); + return ss.str(); + } + + private: + int r_{-1}, g_{-1}, b_{-1}; + float a_{-1.f}; + }; + + enum ELEMENT + { + POLYLINE, + POLYGON, + CIRCLE, + TEXT + }; + + struct Element + { + void fit_into(double xmin, double xmax, double ymin, double ymax, // + double width, double height) + { + // fit bbox[xmin:xmax, ymin:ymax] into viewBox[0:width, 0:height] + double xspan = xmax - xmin; + double yspan = ymax - ymin; + for (auto &pt : points_) { + pt[0] = (pt[0] - xmin) / xspan * width; + pt[1] = (pt[1] - ymin) / yspan * height; + } + } + + protected: + std::vector points_; + Color stroke_{COLOR::BLACK}; + double stroke_width_{1.0}; + Color fill_{COLOR::NONE}; + }; + + struct Polyline : Element + { + Polyline(const std::vector &points) { points_ = points; } + SETUP_FLUENT_API(Polyline, std::vector, points) + SETUP_FLUENT_API_FOR_SVG_ELEMENT(Polyline) + + friend std::ostream &operator<<(std::ostream &out, + const SVG::Polyline &e); + void write(std::ostream &out) const + { + out << ""; + } + std::string to_string() const + { + std::stringstream ss; + write(ss); + return ss.str(); + } + }; + + struct Polygon : Element + { + Polygon(const std::vector &points) { points_ = points; } + SETUP_FLUENT_API(Polygon, std::vector, points) + SETUP_FLUENT_API_FOR_SVG_ELEMENT(Polygon) + + friend std::ostream &operator<<(std::ostream &out, const Polygon &e); + void write(std::ostream &out) const + { + out << ""; + } + std::string to_string() const + { + std::stringstream ss; + write(ss); + return ss.str(); + } + }; + + struct Circle : Element + { + Circle(const PointType ¢er, double r = 1.0) + { + points_ = {center}; + r_ = r; + } + Circle ¢er(const PointType ¢er) + { + points_[0] = center; + return *this; + } + PointType ¢er() { return points_[0]; } + const PointType ¢er() const { return points_[0]; } + Circle &x(double x) + { + points_[0][0] = x; + return *this; + } + double &x() { return points_[0][0]; } + const double &x() const { return points_[0][0]; } + Circle &y(double y) + { + points_[0][0] = y; + return *this; + } + double &y() { return points_[0][0]; } + const double &y() const { return points_[0][0]; } + + SETUP_FLUENT_API(Circle, double, r) + SETUP_FLUENT_API_FOR_SVG_ELEMENT(Circle) + + friend std::ostream &operator<<(std::ostream &out, + const SVG::Circle &e); + void write(std::ostream &out) const + { + out << ""; + } + std::string to_string() const + { + std::stringstream ss; + write(ss); + return ss.str(); + } + + protected: + double r_{1.0}; + }; + + struct Text : Element + { + Text(const PointType &p, const std::string &text, int fontsize = 10.0) + { + points_ = {p}; + text_ = text; + fontsize_ = fontsize; + fill_ = COLOR::BLACK; + } + Text &position(const PointType &p) + { + points_[0] = p; + return *this; + } + PointType &position() { return points_[0]; } + const PointType &position() const { return points_[0]; } + + Text &x(double x) + { + points_[0][0] = x; + return *this; + } + double &x() { return points_[0][0]; } + const double &x() const { return points_[0][0]; } + Text &y(double y) + { + points_[0][0] = y; + return *this; + } + double &y() { return points_[0][0]; } + const double &y() const { return points_[0][0]; } + + SETUP_FLUENT_API(Text, std::string, text) + SETUP_FLUENT_API(Text, std::vector, lines) + SETUP_FLUENT_API(Text, double, fontsize) + SETUP_FLUENT_API_FOR_SVG_ELEMENT(Text) + + // // text-anchor="start" + // + + friend std::ostream &operator<<(std::ostream &out, const SVG::Text &e); + + void write(std::ostream &out) const + { + out << "" << html_escape(text_); + if (!lines_.empty()) { + double fontsize = fontsize_ / 5.0; + for (auto &line : lines_) { + out << "" // + << html_escape(line) << ""; + } + } + out << ""; + } + std::string to_string() const + { + std::stringstream ss; + write(ss); + return ss.str(); + } + + static std::string html_escape(const std::string &text) + { + const static std::vector escapes = { + "&", """, "'", "<", ">"}; + std::map replace; + for (size_t pos = 0; pos != text.size(); ++pos) { + const char c = text[pos]; + if (c == '&') { + replace[pos] = 0; + } else if (c == '\"') { + replace[pos] = 1; + } else if (c == '\'') { + replace[pos] = 2; + } else if (c == '<') { + replace[pos] = 3; + } else if (c == '>') { + replace[pos] = 4; + } + } + if (replace.empty()) { + return text; + } + std::string buffer; + buffer.reserve(text.size() + 6 * replace.size()); + // TODO + for (size_t pos = 0; pos != text.size(); ++pos) { + auto itr = replace.find(text[pos]); + if (itr == replace.end()) { + buffer.append(&text[pos], 1); + } else { + buffer.append(escapes[itr->second]); + } + } + return buffer; + } + + protected: + std::string text_; + std::vector lines_; + double fontsize_{10.0}; + }; + + SVG(double width, double height) : width_(width), height_(height) {} + ~SVG() + { + for (auto &pair : elements_) { + const auto type = pair.first; + if (type == ELEMENT::POLYGON) { + delete (Polygon *)pair.second; + } else if (type == ELEMENT::POLYLINE) { + delete (Polyline *)pair.second; + } else if (type == ELEMENT::CIRCLE) { + delete (Circle *)pair.second; + } else if (type == ELEMENT::TEXT) { + delete (Text *)pair.second; + } + } + } + + SVG clone() const {} + + SETUP_FLUENT_API(SVG, double, width) + SETUP_FLUENT_API(SVG, double, height) + SETUP_FLUENT_API(SVG, double, grid_step) + SETUP_FLUENT_API(SVG, std::vector, grid_x) + SETUP_FLUENT_API(SVG, std::vector, grid_y) + SETUP_FLUENT_API(SVG, Color, grid_color) + SETUP_FLUENT_API(SVG, Color, background) + + Polygon &add_polygon(const std::vector &points) + { + auto ptr = new Polygon(points); + elements_.push_back({ELEMENT::POLYGON, (void *)ptr}); + return *ptr; + } + + Polyline &add_polyline(const std::vector &points) + { + auto ptr = new Polyline(points); + elements_.push_back({ELEMENT::POLYLINE, (void *)ptr}); + return *ptr; + } + + Circle &add_circle(const PointType ¢er, double r = 1.0) + { + auto ptr = new Circle(center, r); + elements_.push_back({ELEMENT::CIRCLE, (void *)ptr}); + return *ptr; + } + + Text &add_text(const PointType &position, const std::string &text, + int fontsize = 10.0) + { + auto ptr = new Text(position, text, fontsize); + elements_.push_back({ELEMENT::TEXT, (void *)ptr}); + return *ptr; + } + + void write(std::ostream &out) const + { + out << ""; + if (!background_.invalid()) { + out << "\n\t"; + } + double xmin = 0.0, xmax = width_, xstep = grid_step_; + double ymin = 0.0, ymax = height_, ystep = grid_step_; + if (grid_x_.size() == 3 && grid_y_.size() == 3) { + xmin = grid_x_[0]; + xmax = grid_x_[1]; + xstep = grid_x_[2]; + ymin = grid_y_[0]; + ymax = grid_y_[1]; + ystep = grid_y_[2]; + } + if (xstep > 0 && ystep > 0 && xmin < xmax && ymin < ymax) { + SVG::Color grid_color = SVG::COLOR::GRAY; + if (!grid_color_.invalid()) { + grid_color = grid_color_; + } + for (double x = xmin; x < xmax; x += xstep) { + out << "\n\t" + << SVG::Polyline({{x, ymin}, {x, ymax}}).stroke(grid_color); + } + for (double y = ymin; y < ymax; y += ystep) { + out << "\n\t" + << SVG::Polyline({{xmin, y}, {xmax, y}}).stroke(grid_color); + } + } + for (auto &pair : elements_) { + out << "\n\t"; + if (pair.first == ELEMENT::POLYGON) { + ((Polygon *)pair.second)->write(out); + } else if (pair.first == ELEMENT::POLYLINE) { + ((Polyline *)pair.second)->write(out); + } else if (pair.first == ELEMENT::CIRCLE) { + ((Circle *)pair.second)->write(out); + } else if (pair.first == ELEMENT::TEXT) { + ((Text *)pair.second)->write(out); + } + } + out << "\n"; + } + + void save(std::string path) const + { + std::ofstream file(path); + write(file); + file.close(); + } + + void fit_to_bbox(double xmin, double xmax, double ymin, double ymax) + { + for (auto &pair : elements_) { + ((Element *)pair.second) + ->fit_into(xmin, ymax, ymin, ymax, width_, height_); + } + } + + private: + // size + double width_, height_; + // grid + double grid_step_{-1.0}; + std::vector grid_x_, grid_y_; // low, high, step + Color grid_color_{COLOR::GRAY}; + // background + Color background_{COLOR::NONE}; + // elements + std::vector> elements_; +}; + +inline std::ostream &operator<<(std::ostream &out, const SVG::Color &c) +{ + c.write(out); + return out; +} + +inline std::ostream &operator<<(std::ostream &out, const SVG::Polyline &p) +{ + p.write(out); + return out; +} + +inline std::ostream &operator<<(std::ostream &out, const SVG::Polygon &p) +{ + p.write(out); + return out; +} + +inline std::ostream &operator<<(std::ostream &out, const SVG::Circle &c) +{ + c.write(out); + return out; +} + +inline std::ostream &operator<<(std::ostream &out, const SVG::Text &t) +{ + t.write(out); + return out; +} + +inline std::ostream &operator<<(std::ostream &out, const SVG &s) +{ + s.write(out); + return out; +} + +} // namespace cubao + +#undef SETUP_FLUENT_API +#undef SETUP_FLUENT_API_FOR_SVG_ELEMENT + +#endif diff --git a/src/pybind11_naive_svg.cpp b/src/pybind11_naive_svg.cpp new file mode 100644 index 0000000..516cf9f --- /dev/null +++ b/src/pybind11_naive_svg.cpp @@ -0,0 +1,45 @@ +// should sync +// - +// https://github.com/cubao/pybind11-naive-svg/blob/master/src/pybind11_naive_svg.cpp +// - +// https://github.com/cubao/headers/tree/main/include/cubao/pybind11_naive_svg.hpp + +#pragma once + +#include +#include +#include + +#include "cubao_inline.hpp" +#include "naive_svg.hpp" + +namespace cubao +{ +namespace py = pybind11; +using namespace pybind11::literals; +using rvp = py::return_value_policy; + +using RowVectorsNx2 = Eigen::Matrix; + +CUBAO_INLINE void bind_naive_svg(py::module &m) +{ + py::class_(m, "Polyline", py::module_local()) // + // + ; + + py::class_(m, "Polygon", py::module_local()) // + // + ; + py::class_(m, "Circle", py::module_local()) // + // + ; + py::class_(m, "Text", py::module_local()) // + // + ; + + py::class_(m, "SVG", py::module_local()) + .def(py::init(), "width"_a, "height"_a) + // + ; +} +} // namespace cubao diff --git a/tests/test_basic.py b/tests/test_basic.py index c1202a5..20b7cc3 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -1,7 +1,15 @@ -import cubao_cmake_example as m +from naive_svg import SVG, Circle, Polygon, Polyline, Text, add -def test_main(): - assert m.__version__ == "0.0.3" - assert m.add(1, 2) == 3 - assert m.subtract(1, 2) == -1 +def test_add(): + assert add(1, 2) == 3 + + +def test_svg(): + svg = SVG(100, 100) + print(svg) + + print(Polyline) + print(Polygon) + print(Circle) + print(Text)