diff options
author | gatecat <gatecat@ds0.me> | 2022-04-08 14:32:33 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-08 14:32:33 +0100 |
commit | 57681e69ce75c781142908cf128bc3f3f59e2f6b (patch) | |
tree | ea642e20bc07441a800944390e1f904e6ce5b113 /common/kernel/svg.cc | |
parent | e42e22575f20b59634f88b5cf694efdb413ff0a0 (diff) | |
parent | 49f178ed94b5fad00d25dbd12adea0bf4732f803 (diff) | |
download | nextpnr-57681e69ce75c781142908cf128bc3f3f59e2f6b.tar.gz nextpnr-57681e69ce75c781142908cf128bc3f3f59e2f6b.tar.bz2 nextpnr-57681e69ce75c781142908cf128bc3f3f59e2f6b.zip |
Merge pull request #973 from YosysHQ/gatecat/folder-tidy
Split up common into kernel,place,route
Diffstat (limited to 'common/kernel/svg.cc')
-rw-r--r-- | common/kernel/svg.cc | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/common/kernel/svg.cc b/common/kernel/svg.cc new file mode 100644 index 00000000..c5e2ea36 --- /dev/null +++ b/common/kernel/svg.cc @@ -0,0 +1,152 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 gatecat <gatecat@ds0.me> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <boost/algorithm/string.hpp> +#include <fstream> +#include "log.h" +#include "nextpnr.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN +namespace { +struct SVGWriter +{ + const Context *ctx; + std::ostream &out; + float scale = 500.0; + bool hide_inactive = false; + SVGWriter(const Context *ctx, std::ostream &out) : ctx(ctx), out(out){}; + const char *get_stroke_colour(GraphicElement::style_t style) + { + switch (style) { + case GraphicElement::STYLE_GRID: + return "#CCC"; + case GraphicElement::STYLE_FRAME: + return "#808080"; + case GraphicElement::STYLE_INACTIVE: + return "#C0C0C0"; + case GraphicElement::STYLE_ACTIVE: + return "#FF3030"; + default: + return "#000"; + } + } + + void write_decal(const DecalXY &dxy) + { + for (const auto &el : ctx->getDecalGraphics(dxy.decal)) { + if (el.style == GraphicElement::STYLE_HIDDEN || + (hide_inactive && el.style == GraphicElement::STYLE_INACTIVE)) + continue; + switch (el.type) { + case GraphicElement::TYPE_LINE: + case GraphicElement::TYPE_ARROW: + case GraphicElement::TYPE_LOCAL_LINE: + case GraphicElement::TYPE_LOCAL_ARROW: + out << stringf("<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" stroke=\"%s\"/>", (el.x1 + dxy.x) * scale, + (el.y1 + dxy.y) * scale, (el.x2 + dxy.x) * scale, (el.y2 + dxy.y) * scale, + get_stroke_colour(el.style)) + << std::endl; + break; + case GraphicElement::TYPE_BOX: + out << stringf("<rect x=\"%f\" y=\"%f\" width=\"%f\" height=\"%f\" stroke=\"%s\" fill=\"%s\"/>", + (el.x1 + dxy.x) * scale, (el.y1 + dxy.y) * scale, (el.x2 - el.x1) * scale, + (el.y2 - el.y1) * scale, get_stroke_colour(el.style), + el.style == GraphicElement::STYLE_ACTIVE ? "#FF8080" : "none") + << std::endl; + break; + default: + break; + } + } + } + + void operator()(const std::string &flags) + { + std::vector<std::string> options; + boost::algorithm::split(options, flags, boost::algorithm::is_space()); + bool noroute = false; + for (const auto &opt : options) { + if (boost::algorithm::starts_with(opt, "scale=")) { + scale = float(std::stod(opt.substr(6))); + continue; + } else if (opt == "hide_routing") { + noroute = true; + } else if (opt == "hide_inactive") { + hide_inactive = true; + } else { + log_error("Unknown SVG option '%s'\n", opt.c_str()); + } + } + float max_x = 0, max_y = 0; + for (auto group : ctx->getGroups()) { + auto decal = ctx->getGroupDecal(group); + for (auto el : ctx->getDecalGraphics(decal.decal)) { + max_x = std::max(max_x, decal.x + el.x1 + 1); + max_y = std::max(max_y, decal.y + el.y1 + 1); + } + } + for (auto bel : ctx->getBels()) { + auto decal = ctx->getBelDecal(bel); + for (auto el : ctx->getDecalGraphics(decal.decal)) { + max_x = std::max(max_x, decal.x + el.x1 + 1); + max_y = std::max(max_y, decal.y + el.y1 + 1); + } + } + for (auto wire : ctx->getWires()) { + auto decal = ctx->getWireDecal(wire); + for (auto el : ctx->getDecalGraphics(decal.decal)) { + max_x = std::max(max_x, decal.x + el.x1 + 1); + max_y = std::max(max_y, decal.y + el.y1 + 1); + } + } + for (auto pip : ctx->getPips()) { + auto decal = ctx->getPipDecal(pip); + for (auto el : ctx->getDecalGraphics(decal.decal)) { + max_x = std::max(max_x, decal.x + el.x1 + 1); + max_y = std::max(max_y, decal.y + el.y1 + 1); + } + } + out << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>" << std::endl; + out << stringf("<svg viewBox=\"0 0 %f %f\" width=\"%f\" height=\"%f\" xmlns=\"http://www.w3.org/2000/svg\">", + max_x * scale, max_y * scale, max_x * scale, max_y * scale) + << std::endl; + out << "<rect x=\"0\" y=\"0\" width=\"100%\" height=\"100%\" stroke=\"#fff\" fill=\"#fff\"/>" << std::endl; + for (auto group : ctx->getGroups()) + write_decal(ctx->getGroupDecal(group)); + for (auto bel : ctx->getBels()) + write_decal(ctx->getBelDecal(bel)); + if (!noroute) { + for (auto wire : ctx->getWires()) + write_decal(ctx->getWireDecal(wire)); + for (auto pip : ctx->getPips()) + write_decal(ctx->getPipDecal(pip)); + } + out << "</svg>" << std::endl; + } +}; +} // namespace + +void Context::writeSVG(const std::string &filename, const std::string &flags) const +{ + std::ofstream out(filename); + SVGWriter(this, out)(flags); +} + +NEXTPNR_NAMESPACE_END |