Added experimental SVG import
This commit is contained in:
parent
fdd42098e3
commit
bd20fe6bf8
38 changed files with 2329 additions and 6 deletions
10
Info.plist
10
Info.plist
|
|
@ -73,6 +73,16 @@
|
|||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>svg</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>SVG file</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSAppleScriptEnabled</key>
|
||||
<true/>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ include(eigen.pri)
|
|||
include(boost.pri)
|
||||
include(glib-2.0.pri)
|
||||
include(gettext.pri)
|
||||
include(libxml2.pri)
|
||||
include(sparkle.pri)
|
||||
include(harfbuzz.pri)
|
||||
include(freetype.pri)
|
||||
|
|
|
|||
35
libxml2.pri
Normal file
35
libxml2.pri
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Detect libxml2, then use this priority list to determine
|
||||
# which library to use:
|
||||
#
|
||||
# Priority
|
||||
# 1. LIBXML2_INCLUDEPATH / LIBXML2_LIBPATH (qmake parameter, not checked it given on commandline)
|
||||
# 2. OPENSCAD_LIBRARIES (environment variable)
|
||||
# 3. system's standard include paths from pkg-config
|
||||
|
||||
libxml2 {
|
||||
# read environment variables
|
||||
OPENSCAD_LIBRARIES_DIR = $$(OPENSCAD_LIBRARIES)
|
||||
LIBXML2_DIR = $$(LIBXML2DIR)
|
||||
|
||||
!isEmpty(OPENSCAD_LIBRARIES_DIR) {
|
||||
isEmpty(LIBXML2_INCLUDEPATH) {
|
||||
LIBXML2_INCLUDEPATH = $$OPENSCAD_LIBRARIES_DIR/include/libxml2
|
||||
LIBXML2_LIBPATH = $$OPENSCAD_LIBRARIES_DIR/lib
|
||||
}
|
||||
}
|
||||
|
||||
isEmpty(LIBXML2_INCLUDEPATH) {
|
||||
LIBXML2_CFLAGS = $$system("pkg-config --cflags libxml-2.0")
|
||||
} else {
|
||||
LIBXML2_CFLAGS = -I$$LIBXML2_INCLUDEPATH
|
||||
}
|
||||
|
||||
isEmpty(LIBXML2_LIBPATH) {
|
||||
LIBXML2_LIBS = $$system("pkg-config --libs libxml-2.0")
|
||||
} else {
|
||||
LIBXML2_LIBS = -L$$LIBXML2_LIBPATH -lxml2
|
||||
}
|
||||
|
||||
QMAKE_CXXFLAGS += $$LIBXML2_CFLAGS
|
||||
LIBS += $$LIBXML2_LIBS
|
||||
}
|
||||
19
openscad.pro
19
openscad.pro
|
|
@ -183,6 +183,7 @@ CONFIG += harfbuzz
|
|||
CONFIG += freetype
|
||||
CONFIG += fontconfig
|
||||
CONFIG += gettext
|
||||
CONFIG += libxml2
|
||||
|
||||
#Uncomment the following line to enable the QScintilla editor
|
||||
!nogui {
|
||||
|
|
@ -351,7 +352,22 @@ HEADERS += src/version_check.h \
|
|||
src/legacyeditor.h \
|
||||
src/LibraryInfoDialog.h
|
||||
|
||||
SOURCES += src/version_check.cc \
|
||||
SOURCES += \
|
||||
src/libsvg/libsvg.cc \
|
||||
src/libsvg/circle.cc \
|
||||
src/libsvg/ellipse.cc \
|
||||
src/libsvg/line.cc \
|
||||
src/libsvg/polygon.cc \
|
||||
src/libsvg/polyline.cc \
|
||||
src/libsvg/rect.cc \
|
||||
src/libsvg/group.cc \
|
||||
src/libsvg/svgpage.cc \
|
||||
src/libsvg/path.cc \
|
||||
src/libsvg/shape.cc \
|
||||
src/libsvg/transformation.cc \
|
||||
src/libsvg/util.cc \
|
||||
\
|
||||
src/version_check.cc \
|
||||
src/ProgressWidget.cc \
|
||||
src/linalg.cc \
|
||||
src/Camera.cc \
|
||||
|
|
@ -436,6 +452,7 @@ SOURCES += src/version_check.cc \
|
|||
src/import.cc \
|
||||
src/import_stl.cc \
|
||||
src/import_off.cc \
|
||||
src/import_svg.cc \
|
||||
src/renderer.cc \
|
||||
src/colormap.cc \
|
||||
src/ThrownTogetherRenderer.cc \
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ Feature::list_t Feature::feature_list;
|
|||
const Feature Feature::ExperimentalEachExpression("lc-each", "Enable <code>each</code> expression in list comprehensions.");
|
||||
const Feature Feature::ExperimentalElseExpression("lc-else", "Enable <code>else</code> expression in list comprehensions.");
|
||||
const Feature Feature::ExperimentalForCExpression("lc-for-c", "Enable C-style <code>for</code> expression in list comprehensions.");
|
||||
const Feature Feature::ExperimentalSvgImport("svg-import", "Enable SVG import.");
|
||||
|
||||
Feature::Feature(const std::string &name, const std::string &description)
|
||||
: enabled(false), name(name), description(description)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ public:
|
|||
static const Feature ExperimentalEachExpression;
|
||||
static const Feature ExperimentalElseExpression;
|
||||
static const Feature ExperimentalForCExpression;
|
||||
static const Feature ExperimentalSvgImport;
|
||||
|
||||
const std::string& get_name() const;
|
||||
const std::string& get_description() const;
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "importnode.h"
|
||||
#include "import.h"
|
||||
#include "importnode.h"
|
||||
|
||||
#include "module.h"
|
||||
#include "ModuleInstantiation.h"
|
||||
|
|
@ -36,6 +36,7 @@
|
|||
#include "dxfdata.h"
|
||||
#include "printutils.h"
|
||||
#include "fileutils.h"
|
||||
#include "feature.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sstream>
|
||||
|
|
@ -98,6 +99,7 @@ AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstanti
|
|||
if (ext == ".stl") actualtype = TYPE_STL;
|
||||
else if (ext == ".off") actualtype = TYPE_OFF;
|
||||
else if (ext == ".dxf") actualtype = TYPE_DXF;
|
||||
else if (Feature::ExperimentalSvgImport.is_enabled() && ext == ".svg") actualtype = TYPE_SVG;
|
||||
}
|
||||
|
||||
ImportNode *node = new ImportNode(inst, actualtype);
|
||||
|
|
@ -127,6 +129,11 @@ AbstractNode *ImportModule::instantiate(const Context *ctx, const ModuleInstanti
|
|||
|
||||
if (node->scale <= 0) node->scale = 1;
|
||||
|
||||
ValuePtr width = c.lookup_variable("width", true);
|
||||
ValuePtr height = c.lookup_variable("height", true);
|
||||
node->width = (width->type() == Value::NUMBER) ? width->toDouble() : -1;
|
||||
node->height = (height->type() == Value::NUMBER) ? height->toDouble() : -1;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
|
@ -148,6 +155,10 @@ const Geometry *ImportNode::createGeometry() const
|
|||
g = p;
|
||||
break;
|
||||
}
|
||||
case TYPE_SVG: {
|
||||
g = import_svg(this->filename);
|
||||
break;
|
||||
}
|
||||
case TYPE_DXF: {
|
||||
DxfData dd(this->fn, this->fs, this->fa, this->filename, this->layername, this->origin_x, this->origin_y, this->scale);
|
||||
g = dd.toPolygon2d();
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@
|
|||
|
||||
class PolySet *import_stl(const std::string &filename);
|
||||
PolySet *import_off(const std::string &filename);
|
||||
const class Polygon2d &import_dxf(const std::string &filename);
|
||||
class Polygon2d *import_svg(const std::string &filename);
|
||||
|
|
|
|||
62
src/import_svg.cc
Normal file
62
src/import_svg.cc
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#include "import.h"
|
||||
#include "Polygon2d.h"
|
||||
#include "printutils.h"
|
||||
#include "libsvg/libsvg.h"
|
||||
#include "clipper-utils.h"
|
||||
|
||||
Polygon2d *import_svg(const std::string &filename)
|
||||
{
|
||||
libsvg::shapes_list_t *shapes = libsvg::libsvg_read_file(filename.c_str());
|
||||
double x_min = 1.0/0.0;
|
||||
double x_max = -1.0/0.0;
|
||||
double y_min = 1.0/0.0;
|
||||
double y_max = -1.0/0.0;
|
||||
for (libsvg::shapes_list_t::iterator it = shapes->begin();it != shapes->end();it++) {
|
||||
PRINTD("SVG shape");
|
||||
libsvg::shape *s = (*it);
|
||||
for (libsvg::path_list_t::iterator it = s->get_path_list().begin();it != s->get_path_list().end();it++) {
|
||||
PRINTD("SVG path");
|
||||
libsvg::path_t& p = *it;
|
||||
for (libsvg::path_t::iterator it2 = p.begin();it2 != p.end();it2++) {
|
||||
Eigen::Vector3d& v = *it2;
|
||||
if (v.x() < x_min) {
|
||||
x_min = v.x();
|
||||
}
|
||||
if (v.x() > x_max) {
|
||||
x_max = v.x();
|
||||
}
|
||||
if (v.y() < y_min) {
|
||||
y_min = v.y();
|
||||
}
|
||||
if (v.y() > y_max) {
|
||||
y_max = v.y();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double cx = (x_min + x_max) / 2;
|
||||
double cy = (y_min + y_max) / 2;
|
||||
|
||||
std::vector<const Polygon2d*> polygons;
|
||||
for (libsvg::shapes_list_t::iterator it = shapes->begin();it != shapes->end();it++) {
|
||||
Polygon2d *poly = new Polygon2d();
|
||||
libsvg::shape *s = (*it);
|
||||
for (libsvg::path_list_t::iterator it = s->get_path_list().begin();it != s->get_path_list().end();it++) {
|
||||
libsvg::path_t& p = *it;
|
||||
|
||||
Outline2d outline;
|
||||
for (libsvg::path_t::iterator it2 = p.begin();it2 != p.end();it2++) {
|
||||
Eigen::Vector3d& v = *it2;
|
||||
double x = v.x() - cx;
|
||||
double y = -v.y() + cy;
|
||||
outline.vertices.push_back(Vector2d(x, y));
|
||||
outline.positive=true;
|
||||
}
|
||||
poly->addOutline(outline);
|
||||
}
|
||||
polygons.push_back(poly);
|
||||
}
|
||||
return ClipperUtils::apply(polygons, ClipperLib::ctUnion);
|
||||
}
|
||||
|
||||
|
|
@ -7,6 +7,7 @@ enum import_type_e {
|
|||
TYPE_UNKNOWN,
|
||||
TYPE_STL,
|
||||
TYPE_OFF,
|
||||
TYPE_SVG,
|
||||
TYPE_DXF
|
||||
};
|
||||
|
||||
|
|
@ -24,5 +25,6 @@ public:
|
|||
int convexity;
|
||||
double fn, fs, fa;
|
||||
double origin_x, origin_y, scale;
|
||||
double width, height;
|
||||
virtual const class Geometry *createGeometry() const;
|
||||
};
|
||||
|
|
|
|||
43
src/libsvg/circle.cc
Normal file
43
src/libsvg/circle.cc
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
#include "circle.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
const std::string circle::name("circle");
|
||||
|
||||
circle::circle()
|
||||
{
|
||||
}
|
||||
|
||||
circle::circle(const circle& orig) : shape(orig)
|
||||
{
|
||||
r = orig.r;
|
||||
}
|
||||
|
||||
circle::~circle()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
circle::set_attrs(attr_map_t& attrs)
|
||||
{
|
||||
shape::set_attrs(attrs);
|
||||
this->x = parse_double(attrs["cx"]);
|
||||
this->y = parse_double(attrs["cy"]);
|
||||
this->r = parse_double(attrs["r"]);
|
||||
|
||||
path_t path;
|
||||
draw_ellipse(path, get_x(), get_y(), get_radius(), get_radius());
|
||||
path_list.push_back(path);
|
||||
}
|
||||
|
||||
void
|
||||
circle::dump()
|
||||
{
|
||||
std::cout << get_name()
|
||||
<< ": x = " << this->x
|
||||
<< ": y = " << this->y
|
||||
<< ": r = " << this->r
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
29
src/libsvg/circle.h
Normal file
29
src/libsvg/circle.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef LIBSVG_CIRCLE_H
|
||||
#define LIBSVG_CIRCLE_H
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
class circle : public shape {
|
||||
protected:
|
||||
double r;
|
||||
|
||||
public:
|
||||
circle();
|
||||
circle(const circle& orig);
|
||||
virtual ~circle();
|
||||
|
||||
virtual double get_radius() { return r; }
|
||||
|
||||
virtual void set_attrs(attr_map_t& attrs);
|
||||
virtual void dump();
|
||||
const std::string& get_name() const { return circle::name; };
|
||||
|
||||
static const std::string name;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LIBSVG_CIRCLE_H */
|
||||
|
||||
49
src/libsvg/ellipse.cc
Normal file
49
src/libsvg/ellipse.cc
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "ellipse.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
const std::string ellipse::name("ellipse");
|
||||
|
||||
ellipse::ellipse()
|
||||
{
|
||||
}
|
||||
|
||||
ellipse::ellipse(const ellipse& orig) : shape(orig)
|
||||
{
|
||||
rx = orig.rx;
|
||||
ry = orig.ry;
|
||||
}
|
||||
|
||||
ellipse::~ellipse()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ellipse::set_attrs(attr_map_t& attrs)
|
||||
{
|
||||
shape::set_attrs(attrs);
|
||||
this->x = parse_double(attrs["cx"]);
|
||||
this->y = parse_double(attrs["cy"]);
|
||||
this->rx = parse_double(attrs["rx"]);
|
||||
this->ry = parse_double(attrs["ry"]);
|
||||
|
||||
path_t path;
|
||||
draw_ellipse(path, get_x(), get_y(), get_radius_x(), get_radius_y());
|
||||
path_list.push_back(path);
|
||||
}
|
||||
|
||||
void
|
||||
ellipse::dump()
|
||||
{
|
||||
std::cout << get_name()
|
||||
<< ": x = " << this->x
|
||||
<< ": y = " << this->y
|
||||
<< ": rx = " << this->rx
|
||||
<< ": ry = " << this->ry
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
31
src/libsvg/ellipse.h
Normal file
31
src/libsvg/ellipse.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef LIBSVG_ELLIPSE_H
|
||||
#define LIBSVG_ELLIPSE_H
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
class ellipse : public shape {
|
||||
protected:
|
||||
double rx;
|
||||
double ry;
|
||||
|
||||
public:
|
||||
ellipse();
|
||||
ellipse(const ellipse& orig);
|
||||
virtual ~ellipse();
|
||||
|
||||
virtual double get_radius_x() { return rx; }
|
||||
virtual double get_radius_y() { return ry; }
|
||||
|
||||
virtual void set_attrs(attr_map_t& attrs);
|
||||
virtual void dump();
|
||||
const std::string& get_name() const { return ellipse::name; };
|
||||
|
||||
static const std::string name;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LIBSVG_ELLIPSE_H */
|
||||
|
||||
37
src/libsvg/group.cc
Normal file
37
src/libsvg/group.cc
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "group.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
const std::string group::name("g");
|
||||
|
||||
group::group()
|
||||
{
|
||||
}
|
||||
|
||||
group::group(const group& orig) : shape(orig)
|
||||
{
|
||||
}
|
||||
|
||||
group::~group()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
group::set_attrs(attr_map_t& attrs)
|
||||
{
|
||||
shape::set_attrs(attrs);
|
||||
}
|
||||
|
||||
void
|
||||
group::dump()
|
||||
{
|
||||
std::cout << get_name()
|
||||
<< ": x = " << this->x
|
||||
<< ": y = " << this->y
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
28
src/libsvg/group.h
Normal file
28
src/libsvg/group.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#ifndef LIBSVG_GROUP_H
|
||||
#define LIBSVG_GROUP_H
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
class group : public shape {
|
||||
protected:
|
||||
|
||||
public:
|
||||
group();
|
||||
group(const group& orig);
|
||||
virtual ~group();
|
||||
|
||||
virtual bool is_container() { return true; }
|
||||
|
||||
virtual void set_attrs(attr_map_t& attrs);
|
||||
virtual void dump();
|
||||
const std::string& get_name() const { return group::name; };
|
||||
|
||||
static const std::string name;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LIBSVG_GROUP_H */
|
||||
|
||||
171
src/libsvg/libsvg.cc
Normal file
171
src/libsvg/libsvg.cc
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
#include <map>
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Geometry>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <libxml/xmlreader.h>
|
||||
|
||||
#include "libsvg.h"
|
||||
|
||||
#include "shape.h"
|
||||
#include "circle.h"
|
||||
#include "ellipse.h"
|
||||
#include "line.h"
|
||||
#include "polygon.h"
|
||||
#include "polyline.h"
|
||||
#include "rect.h"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
fs::path path("/");
|
||||
|
||||
typedef void (*cb_func)(const xmlChar *);
|
||||
|
||||
std::map<const std::string, cb_func> funcs;
|
||||
std::map<const std::string, cb_func> end_funcs;
|
||||
|
||||
static bool in_defs = false;
|
||||
static std::stack<shape *> shapes;
|
||||
static shapes_list_t *shape_list;
|
||||
|
||||
attr_map_t read_attributes(xmlTextReaderPtr reader)
|
||||
{
|
||||
attr_map_t attrs;
|
||||
int attr_count = xmlTextReaderAttributeCount(reader);
|
||||
for (int idx = 0;idx < attr_count;idx++) {
|
||||
xmlTextReaderMoveToAttributeNo(reader, idx);
|
||||
const char *name = reinterpret_cast<const char *> (xmlTextReaderName(reader));
|
||||
const char *value = reinterpret_cast<const char *> (xmlTextReaderValue(reader));
|
||||
attrs[name] = value;
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
|
||||
void processNode(xmlTextReaderPtr reader)
|
||||
{
|
||||
const char *name = reinterpret_cast<const char *> (xmlTextReaderName(reader));
|
||||
if (name == NULL)
|
||||
name = reinterpret_cast<const char *> (xmlStrdup(BAD_CAST "--"));
|
||||
|
||||
bool isEmpty;
|
||||
xmlChar *value = xmlTextReaderValue(reader);
|
||||
int node_type = xmlTextReaderNodeType(reader);
|
||||
switch (node_type) {
|
||||
case XML_READER_TYPE_ELEMENT:
|
||||
isEmpty = xmlTextReaderIsEmptyElement(reader);
|
||||
{
|
||||
path /= name;
|
||||
|
||||
if (std::string("defs") == name) {
|
||||
in_defs = true;
|
||||
}
|
||||
|
||||
shape *s = shape::create_from_name(name);
|
||||
if (!in_defs && s) {
|
||||
attr_map_t attrs = read_attributes(reader);
|
||||
s->set_attrs(attrs);
|
||||
shape_list->push_back(s);
|
||||
if (!shapes.empty()) {
|
||||
shapes.top()->add_child(s);
|
||||
}
|
||||
if (s->is_container()) {
|
||||
shapes.push(s);
|
||||
}
|
||||
s->apply_transform();
|
||||
}
|
||||
}
|
||||
if (!isEmpty) {
|
||||
break;
|
||||
}
|
||||
case XML_READER_TYPE_END_ELEMENT:
|
||||
{
|
||||
if (std::string("defs") == name) {
|
||||
in_defs = false;
|
||||
} else if (std::string("g") == name) {
|
||||
shapes.pop();
|
||||
}
|
||||
cb_func f1 = end_funcs[path.string()];
|
||||
if (f1) {
|
||||
f1(value);
|
||||
}
|
||||
path = path.parent_path();
|
||||
}
|
||||
break;
|
||||
case XML_READER_TYPE_TEXT:
|
||||
{
|
||||
// printf("%d %d %s %s\n",
|
||||
// xmlTextReaderDepth(reader),
|
||||
// xmlTextReaderNodeType(reader),
|
||||
// path.c_str(),
|
||||
// value);
|
||||
cb_func f = funcs[path.string()];
|
||||
if (f) {
|
||||
f(value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
xmlFree(value);
|
||||
xmlFree((void *) (name));
|
||||
}
|
||||
|
||||
int streamFile(const char *filename)
|
||||
{
|
||||
xmlTextReaderPtr reader;
|
||||
|
||||
in_defs = false;
|
||||
reader = xmlNewTextReaderFilename(filename);
|
||||
xmlTextReaderSetParserProp(reader, XML_PARSER_SUBST_ENTITIES, 1);
|
||||
if (reader != NULL) {
|
||||
int ret = xmlTextReaderRead(reader);
|
||||
while (ret == 1) {
|
||||
processNode(reader);
|
||||
ret = xmlTextReaderRead(reader);
|
||||
}
|
||||
xmlFreeTextReader(reader);
|
||||
if (ret != 0) {
|
||||
printf("%s : failed to parse\n", filename);
|
||||
}
|
||||
} else {
|
||||
printf("Unable to open %s\n", filename);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dump(int idx, shape *s) {
|
||||
for (int a = 0;a < idx;a++) {
|
||||
std::cout << " ";
|
||||
}
|
||||
std::cout << "=> " << *s << std::endl;
|
||||
std::vector<shape *> children = s->get_children();
|
||||
for (std::vector<shape *>::iterator it = children.begin();it != children.end();it++) {
|
||||
dump(idx + 1, *it);
|
||||
}
|
||||
}
|
||||
|
||||
shapes_list_t *
|
||||
libsvg_read_file(const char *filename)
|
||||
{
|
||||
shape_list = new shapes_list_t();
|
||||
streamFile(filename);
|
||||
|
||||
//#ifdef DEBUG
|
||||
// if (!shape_list->empty()) {
|
||||
// dump(0, (*shape_list)[0]);
|
||||
// }
|
||||
//#endif
|
||||
|
||||
return shape_list;
|
||||
}
|
||||
|
||||
void
|
||||
libsvg_free(shapes_list_t *shapes)
|
||||
{
|
||||
delete shapes;
|
||||
}
|
||||
|
||||
}
|
||||
21
src/libsvg/libsvg.h
Normal file
21
src/libsvg/libsvg.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#ifndef LIBSVG_LIBSVG_H
|
||||
#define LIBSVG_LIBSVG_H
|
||||
|
||||
#include "shape.h"
|
||||
#include "rect.h"
|
||||
#include "path.h"
|
||||
#include "svgpage.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
typedef std::vector<shape *> shapes_list_t;
|
||||
|
||||
shapes_list_t *
|
||||
libsvg_read_file(const char *filename);
|
||||
|
||||
void
|
||||
libsvg_free(shapes_list_t *shapes);
|
||||
|
||||
}
|
||||
|
||||
#endif /* LIBSVG_LIBSVG_H */
|
||||
47
src/libsvg/line.cc
Normal file
47
src/libsvg/line.cc
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#include "line.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
const std::string line::name("line");
|
||||
|
||||
line::line()
|
||||
{
|
||||
}
|
||||
|
||||
line::line(const line& orig) : shape(orig)
|
||||
{
|
||||
x2 = orig.x2;
|
||||
y2 = orig.y2;
|
||||
}
|
||||
|
||||
line::~line()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
line::set_attrs(attr_map_t& attrs)
|
||||
{
|
||||
shape::set_attrs(attrs);
|
||||
this->x = parse_double(attrs["x1"]);
|
||||
this->y = parse_double(attrs["y1"]);
|
||||
this->x2 = parse_double(attrs["x2"]);
|
||||
this->y2 = parse_double(attrs["y2"]);
|
||||
|
||||
path_t path;
|
||||
path.push_back(Eigen::Vector3d(x, y, 0));
|
||||
path.push_back(Eigen::Vector3d(x2, y2, 0));
|
||||
offset_path(path_list, path, get_stroke_width(), get_stroke_linecap());
|
||||
}
|
||||
|
||||
void
|
||||
line::dump()
|
||||
{
|
||||
std::cout << get_name()
|
||||
<< ": x1 = " << this->x
|
||||
<< ": y1 = " << this->y
|
||||
<< ": x2 = " << this->x2
|
||||
<< ": y2 = " << this->y2
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
31
src/libsvg/line.h
Normal file
31
src/libsvg/line.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef LIBSVG_LINE_H
|
||||
#define LIBSVG_LINE_H
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
class line : public shape {
|
||||
private:
|
||||
double x2;
|
||||
double y2;
|
||||
|
||||
public:
|
||||
line();
|
||||
line(const line& orig);
|
||||
virtual ~line();
|
||||
|
||||
virtual double get_x2() { return x2; }
|
||||
virtual double get_y2() { return y2; }
|
||||
|
||||
virtual void set_attrs(attr_map_t& attrs);
|
||||
virtual void dump();
|
||||
const std::string& get_name() const { return line::name; };
|
||||
|
||||
static const std::string name;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LIBSVG_LINE_H */
|
||||
|
||||
488
src/libsvg/path.cc
Normal file
488
src/libsvg/path.cc
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "path.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
const std::string path::name("path");
|
||||
|
||||
/*
|
||||
PATHSEG_CLOSEPATH z
|
||||
PATHSEG_MOVETO_ABS M
|
||||
PATHSEG_MOVETO_REL m
|
||||
PATHSEG_LINETO_ABS L
|
||||
PATHSEG_LINETO_REL l
|
||||
PATHSEG_CURVETO_CUBIC_ABS C
|
||||
PATHSEG_CURVETO_CUBIC_REL c
|
||||
PATHSEG_CURVETO_QUADRATIC_ABS Q
|
||||
PATHSEG_CURVETO_QUADRATIC_REL q
|
||||
PATHSEG_ARC_ABS A
|
||||
PATHSEG_ARC_REL a
|
||||
PATHSEG_LINETO_HORIZONTAL_ABS H
|
||||
PATHSEG_LINETO_HORIZONTAL_REL h
|
||||
PATHSEG_LINETO_VERTICAL_ABS V
|
||||
PATHSEG_LINETO_VERTICAL_REL v
|
||||
PATHSEG_CURVETO_CUBIC_SMOOTH_ABS S
|
||||
PATHSEG_CURVETO_CUBIC_SMOOTH_REL s
|
||||
PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS T
|
||||
PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL t
|
||||
*/
|
||||
|
||||
path::path()
|
||||
{
|
||||
}
|
||||
|
||||
path::path(const path& orig) : shape(orig)
|
||||
{
|
||||
data = orig.data;
|
||||
}
|
||||
|
||||
path::~path()
|
||||
{
|
||||
}
|
||||
|
||||
static double
|
||||
vector_angle(double ux, double uy, double vx, double vy)
|
||||
{
|
||||
double angle = atan2(vy, vx) - atan2(uy, ux);
|
||||
if (angle < 0) {
|
||||
angle += 2 * M_PI;
|
||||
}
|
||||
return angle;
|
||||
}
|
||||
|
||||
void
|
||||
path::arc_to(path_t& path, double x1, double y1, double rx, double ry, double x2, double y2, double angle, bool large, bool sweep)
|
||||
{
|
||||
// http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
|
||||
|
||||
// (F.6.5.1))
|
||||
double cos_rad = cos(M_PI * angle / 180.0);
|
||||
double sin_rad = sin(M_PI * angle / 180.0);
|
||||
double dx = (x1 - x2) / 2;
|
||||
double dy = (y1 - y2) / 2;
|
||||
double x1_ = cos_rad * dx + sin_rad * dy;
|
||||
double y1_ = -sin_rad * dx + cos_rad * dy;
|
||||
|
||||
double d = (x1_ * x1_) / (rx * rx) + (y1_ * y1_) / (ry * ry);
|
||||
if (d > 1) {
|
||||
rx = abs(sqrt(d) * rx);
|
||||
ry = abs(sqrt(d) * ry);
|
||||
}
|
||||
|
||||
// F.6.5.2
|
||||
double t1 = rx * rx * ry * ry - rx * rx * y1_ * y1_ - ry * ry * x1_ * x1_;
|
||||
double t2 = rx * rx * y1_ * y1_ + ry * ry * x1_ * x1_;
|
||||
if (t1 < 0) {
|
||||
t1 = 0;
|
||||
}
|
||||
double t3 = sqrt(t1 / t2);
|
||||
if (large == sweep) {
|
||||
t3 = -t3;
|
||||
}
|
||||
|
||||
double cx_ = t3 * rx * y1_ / ry;
|
||||
double cy_ = t3 * -ry * x1_ / rx;
|
||||
|
||||
// F.6.5.3
|
||||
double cx = cos_rad * cx_ - sin_rad * cy_ + (x1 + x2) / 2.0;
|
||||
double cy = sin_rad * cx_ + cos_rad * cy_ + (y1 + y2) / 2.0;
|
||||
|
||||
// F.6.5.4
|
||||
double ux = (x1_ - cx_) / rx;
|
||||
double uy = (y1_ - cy_) / ry;
|
||||
double vx = (-x1_ - cx_) / rx;
|
||||
double vy = (-y1_ - cy_) / ry;
|
||||
|
||||
double theta = vector_angle(1, 0, ux, uy);
|
||||
double delta = vector_angle(ux, uy, vx, vy);
|
||||
if (!sweep) {
|
||||
delta -= 2 * M_PI;
|
||||
}
|
||||
|
||||
int steps = std::abs(delta) * 10.0 / M_PI + 4;
|
||||
for (int a = 0;a <= steps;a++) {
|
||||
double phi = theta + delta * a / steps;
|
||||
|
||||
double xx = cos_rad * cos(phi) * rx - sin_rad * sin(phi) * ry;
|
||||
double yy = sin_rad * cos(phi) * rx + cos_rad * sin(phi) * ry;
|
||||
|
||||
path.push_back(Eigen::Vector3d(xx + cx, yy + cy, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
path::curve_to(path_t& path, double x, double y, double cx1, double cy1, double x2, double y2)
|
||||
{
|
||||
unsigned long fn = 20;
|
||||
for (unsigned long idx = 1;idx <= fn;idx++) {
|
||||
const double a = idx * (1.0 / (double)fn);
|
||||
const double xx = x * t(a, 2) + cx1 * 2 * t(a, 1) * a + x2 * a * a;
|
||||
const double yy = y * t(a, 2) + cy1 * 2 * t(a, 1) * a + y2 * a * a;
|
||||
path.push_back(Eigen::Vector3d(xx, yy, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
path::curve_to(path_t& path, double x, double y, double cx1, double cy1, double cx2, double cy2, double x2, double y2)
|
||||
{
|
||||
unsigned long fn = 20;
|
||||
for (unsigned long idx = 1;idx <= fn;idx++) {
|
||||
const double a = idx * (1.0 / (double)fn);
|
||||
const double xx = x * t(a, 3) + cx1 * 3 * t(a, 2) * a + cx2 * 3 * t(a, 1) * a * a + x2 * a * a * a;
|
||||
const double yy = y * t(a, 3) + cy1 * 3 * t(a, 2) * a + cy2 * 3 * t(a, 1) * a * a + y2 * a * a * a;
|
||||
path.push_back(Eigen::Vector3d(xx, yy, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
path::set_attrs(attr_map_t& attrs)
|
||||
{
|
||||
std::string commands = "-zmlcqahvstZMLCQAHVST";
|
||||
|
||||
shape::set_attrs(attrs);
|
||||
this->data = attrs["d"];
|
||||
|
||||
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
|
||||
boost::char_separator<char> sep(" ,", commands.c_str());
|
||||
tokenizer tokens(this->data, sep);
|
||||
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
double xx = 0;
|
||||
double yy = 0;
|
||||
double rx = 0;
|
||||
double ry = 0;
|
||||
double cx1 = 0;
|
||||
double cy1 = 0;
|
||||
double cx2 = 0;
|
||||
double cy2 = 0;
|
||||
double angle = 0;
|
||||
bool large = false;
|
||||
bool sweep = false;
|
||||
bool last_cmd_cubic_bezier = false;
|
||||
bool last_cmd_quadratic_bezier = false;
|
||||
char cmd = ' ';
|
||||
int point = 0;
|
||||
|
||||
bool negate = false;
|
||||
bool path_closed = false;
|
||||
std::string exp;
|
||||
path_list.push_back(path_t());
|
||||
for (tokenizer::iterator it = tokens.begin();it != tokens.end();++it) {
|
||||
std::string v = (*it);
|
||||
|
||||
double p = 0;
|
||||
if ((v.length() == 1) && (commands.find(v) != std::string::npos)) {
|
||||
if (v[0] == '-') {
|
||||
negate = true;
|
||||
continue;
|
||||
}
|
||||
point = -1;
|
||||
cmd = v[0];
|
||||
} else {
|
||||
if (*v.rbegin() == 'e') {
|
||||
exp = negate ? std::string("-") + v : v;
|
||||
negate = false;
|
||||
continue;
|
||||
}
|
||||
if (exp.empty()) {
|
||||
p = parse_double(v);
|
||||
p = negate ? -p : p;
|
||||
} else {
|
||||
p = parse_double(exp + (negate ? "-" : "") + v);
|
||||
exp = "";
|
||||
}
|
||||
negate = false;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case 'a':
|
||||
case 'A':
|
||||
//(rx ry x-axis-rotation large-arc-flag sweep-flag x y)
|
||||
switch (point) {
|
||||
case 0:
|
||||
rx = abs(p);
|
||||
break;
|
||||
case 1:
|
||||
ry = abs(p);
|
||||
break;
|
||||
case 2:
|
||||
angle = p;
|
||||
break;
|
||||
case 3:
|
||||
large = p > 0.5;
|
||||
break;
|
||||
case 4:
|
||||
sweep = p > 0.5;
|
||||
break;
|
||||
case 5:
|
||||
xx = cmd == 'a' ? x + p : p;
|
||||
break;
|
||||
case 6:
|
||||
yy = cmd == 'a' ? y + p : p;
|
||||
arc_to(path_list.back(), x, y, rx, ry, xx, yy, angle, large, sweep);
|
||||
x = xx;
|
||||
y = yy;
|
||||
point = -1;
|
||||
last_cmd_cubic_bezier = false;
|
||||
last_cmd_quadratic_bezier = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
case 'L':
|
||||
switch (point) {
|
||||
case 0:
|
||||
xx = cmd == 'l' ? x + p : p;
|
||||
break;
|
||||
case 1:
|
||||
yy = cmd == 'l' ? y + p : p;
|
||||
path_list.back().push_back(Eigen::Vector3d(xx, yy, 0));
|
||||
x = xx;
|
||||
y = yy;
|
||||
point = -1;
|
||||
last_cmd_cubic_bezier = false;
|
||||
last_cmd_quadratic_bezier = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'c':
|
||||
case 'C':
|
||||
switch (point) {
|
||||
case 0:
|
||||
cx1 = p;
|
||||
break;
|
||||
case 1:
|
||||
cy1 = p;
|
||||
break;
|
||||
case 2:
|
||||
cx2 = p;
|
||||
break;
|
||||
case 3:
|
||||
cy2 = p;
|
||||
break;
|
||||
case 4:
|
||||
xx = cmd == 'c' ? x + p : p;
|
||||
break;
|
||||
case 5:
|
||||
yy = cmd == 'c' ? y + p : p;
|
||||
cx1 = cmd == 'c' ? x + cx1 : cx1;
|
||||
cy1 = cmd == 'c' ? y + cy1 : cy1;
|
||||
cx2 = cmd == 'c' ? x + cx2 : cx2;
|
||||
cy2 = cmd == 'c' ? y + cy2 : cy2;
|
||||
curve_to(path_list.back(), x, y, cx1, cy1, cx2, cy2, xx, yy);
|
||||
x = xx;
|
||||
y = yy;
|
||||
point = -1;
|
||||
last_cmd_cubic_bezier = true;
|
||||
last_cmd_quadratic_bezier = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
case 'S':
|
||||
switch (point) {
|
||||
case 0:
|
||||
if (last_cmd_cubic_bezier) {
|
||||
Eigen::Vector2d old_control_point(cx2, cy2);
|
||||
Eigen::Vector2d current_point(x, y);
|
||||
Eigen::Vector2d new_control_point = current_point + (current_point - old_control_point);
|
||||
cx1 = new_control_point.x();
|
||||
cy1 = new_control_point.y();
|
||||
} else {
|
||||
cx1 = x;
|
||||
cy1 = y;
|
||||
}
|
||||
cx2 = p;
|
||||
break;
|
||||
case 1:
|
||||
cy2 = p;
|
||||
break;
|
||||
case 2:
|
||||
xx = cmd == 's' ? x + p : p;
|
||||
break;
|
||||
case 3:
|
||||
yy = cmd == 's' ? y + p : p;
|
||||
cx2 = cmd == 's' ? x + cx2 : cx2;
|
||||
cy2 = cmd == 's' ? y + cy2 : cy2;
|
||||
curve_to(path_list.back(), x, y, cx1, cy1, cx2, cy2, xx, yy);
|
||||
x = xx;
|
||||
y = yy;
|
||||
point = -1;
|
||||
last_cmd_cubic_bezier = true;
|
||||
last_cmd_quadratic_bezier = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'q':
|
||||
case 'Q':
|
||||
switch (point) {
|
||||
case 0:
|
||||
cx1 = p;
|
||||
break;
|
||||
case 1:
|
||||
cy1 = p;
|
||||
break;
|
||||
case 2:
|
||||
xx = cmd == 'q' ? x + p : p;
|
||||
break;
|
||||
case 3:
|
||||
yy = cmd == 'q' ? y + p : p;
|
||||
cx1 = cmd == 'q' ? x + cx1 : cx1;
|
||||
cy1 = cmd == 'q' ? y + cy1 : cy1;
|
||||
curve_to(path_list.back(), x, y, cx1, cy1, xx, yy);
|
||||
x = xx;
|
||||
y = yy;
|
||||
point = -1;
|
||||
last_cmd_cubic_bezier = false;
|
||||
last_cmd_quadratic_bezier = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
case 'T':
|
||||
switch (point) {
|
||||
case 0:
|
||||
if (last_cmd_quadratic_bezier) {
|
||||
Eigen::Vector2d old_control_point(cx1, cy1);
|
||||
Eigen::Vector2d current_point(x, y);
|
||||
Eigen::Vector2d new_control_point = current_point + (current_point - old_control_point);
|
||||
cx1 = new_control_point.x();
|
||||
cy1 = new_control_point.y();
|
||||
} else {
|
||||
cx1 = x;
|
||||
cy1 = y;
|
||||
}
|
||||
xx = cmd == 't' ? x + p : p;
|
||||
break;
|
||||
case 1:
|
||||
yy = cmd == 't' ? y + p : p;
|
||||
curve_to(path_list.back(), x, y, cx1, cy1, xx, yy);
|
||||
x = xx;
|
||||
y = yy;
|
||||
point = -1;
|
||||
last_cmd_cubic_bezier = false;
|
||||
last_cmd_quadratic_bezier = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
case 'M':
|
||||
switch (point) {
|
||||
case 0:
|
||||
xx = cmd == 'm' ? x + p : p;
|
||||
break;
|
||||
case 1:
|
||||
yy = cmd == 'm' ? y + p : p;
|
||||
cmd = cmd == 'm' ? 'l' : 'L';
|
||||
|
||||
path_t path = path_list.back();
|
||||
if (!path_list.back().empty()) {
|
||||
if (is_open_path(path)) {
|
||||
path_list.pop_back();
|
||||
offset_path(path_list, path, get_stroke_width(), get_stroke_linecap());
|
||||
}
|
||||
path_list.push_back(path_t());
|
||||
}
|
||||
|
||||
path_list.back().push_back(Eigen::Vector3d(xx, yy, 0));
|
||||
x = xx;
|
||||
y = yy;
|
||||
point = -1;
|
||||
last_cmd_cubic_bezier = false;
|
||||
last_cmd_quadratic_bezier = false;
|
||||
}
|
||||
break;
|
||||
case 'v':
|
||||
case 'V':
|
||||
switch (point) {
|
||||
case 0:
|
||||
y = cmd == 'v' ? y + p : p;
|
||||
path_list.back().push_back(Eigen::Vector3d(x, y, 0));
|
||||
point = -1;
|
||||
last_cmd_cubic_bezier = false;
|
||||
last_cmd_quadratic_bezier = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
case 'H':
|
||||
switch (point) {
|
||||
case 0:
|
||||
x = cmd == 'h' ? x + p : p;
|
||||
path_list.back().push_back(Eigen::Vector3d(x, y, 0));
|
||||
point = -1;
|
||||
last_cmd_cubic_bezier = false;
|
||||
last_cmd_quadratic_bezier = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'z':
|
||||
case 'Z':
|
||||
if (!path_list.back().empty()) {
|
||||
Eigen::Vector3d p = path_list.back()[0];
|
||||
path_list.back().push_back(p);
|
||||
x = p.x();
|
||||
y = p.y();
|
||||
}
|
||||
path_list.push_back(path_t());
|
||||
path_closed = true;
|
||||
last_cmd_cubic_bezier = false;
|
||||
last_cmd_quadratic_bezier = false;
|
||||
break;
|
||||
}
|
||||
|
||||
point++;
|
||||
}
|
||||
|
||||
while (!path_list.empty() && path_list.back().empty()) {
|
||||
path_list.pop_back();
|
||||
}
|
||||
|
||||
if (!path_closed && !path_list.empty()) {
|
||||
path_t path = path_list.back();
|
||||
if (is_open_path(path)) {
|
||||
path_list.pop_back();
|
||||
offset_path(path_list, path, get_stroke_width(), get_stroke_linecap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
path::is_open_path(path_t& path)
|
||||
{
|
||||
const Eigen::Vector3d p1 = path[0];
|
||||
const Eigen::Vector3d p2 = path.back();
|
||||
double distance = pow(pow(p1.x() - p2.x(), 2) + pow(p1.y() - p2.y(), 2) + pow(p1.z() - p2.z(), 2), 0.5);
|
||||
return distance > 0.1;
|
||||
}
|
||||
|
||||
void
|
||||
path::dump()
|
||||
{
|
||||
std::cout << get_name()
|
||||
<< ": x = " << this->x
|
||||
<< ", y = " << this->y;
|
||||
for (path_list_t::iterator it = path_list.begin();it != path_list.end();it++) {
|
||||
path_t& p = *it;
|
||||
std::cout << "[";
|
||||
for (path_t::iterator it2 = p.begin();it2 != p.end();it2++) {
|
||||
Eigen::Vector3d& v = *it2;
|
||||
std::cout << " (" << v.x() << ", " << v.y() << ")";
|
||||
}
|
||||
std::cout << "]";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
41
src/libsvg/path.h
Normal file
41
src/libsvg/path.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef LIBSVG_PATH_H
|
||||
#define LIBSVG_PATH_H
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
class path : public shape {
|
||||
protected:
|
||||
std::string data;
|
||||
|
||||
private:
|
||||
inline double t(double t, int exp) const {
|
||||
return pow(1.0 - t, exp);
|
||||
}
|
||||
|
||||
bool is_open_path(path_t& path);
|
||||
void arc_to(path_t& path, double x, double y, double rx, double ry, double x2, double y2, double angle, bool large, bool sweep);
|
||||
void curve_to(path_t& path, double x, double y, double cx1, double cy1, double x2, double y2);
|
||||
void curve_to(path_t& path, double x, double y, double cx1, double cy1, double cx2, double cy2, double x2, double y2);
|
||||
|
||||
public:
|
||||
path();
|
||||
path(const path& orig);
|
||||
virtual ~path();
|
||||
|
||||
virtual void set_attrs(attr_map_t& attrs);
|
||||
virtual void dump();
|
||||
const std::string& get_name() const { return path::name; };
|
||||
|
||||
static const std::string name;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LIBSVG_PATH_H */
|
||||
|
||||
52
src/libsvg/polygon.cc
Normal file
52
src/libsvg/polygon.cc
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#include <boost/tokenizer.hpp>
|
||||
|
||||
#include "polygon.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
const std::string polygon::name("polygon");
|
||||
|
||||
polygon::polygon()
|
||||
{
|
||||
}
|
||||
|
||||
polygon::polygon(const polygon& orig) : shape(orig)
|
||||
{
|
||||
points = orig.points;
|
||||
}
|
||||
|
||||
polygon::~polygon()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
polygon::set_attrs(attr_map_t& attrs)
|
||||
{
|
||||
shape::set_attrs(attrs);
|
||||
this->points = attrs["points"];
|
||||
|
||||
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
|
||||
boost::char_separator<char> sep(" ,");
|
||||
tokenizer tokens(this->points, sep);
|
||||
|
||||
double x = 0.0;
|
||||
path_t path;
|
||||
bool first = true;
|
||||
for (tokenizer::iterator it = tokens.begin();it != tokens.end();++it) {
|
||||
std::string v = (*it);
|
||||
double p = parse_double(v);
|
||||
|
||||
if (first) {
|
||||
x = p;
|
||||
} else {
|
||||
path.push_back(Eigen::Vector3d(x, p, 0));
|
||||
}
|
||||
first = !first;
|
||||
}
|
||||
if (!path.empty()) {
|
||||
path.push_back(path[0]);
|
||||
}
|
||||
path_list.push_back(path);
|
||||
}
|
||||
|
||||
}
|
||||
26
src/libsvg/polygon.h
Normal file
26
src/libsvg/polygon.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef LIBSVG_POLYGON_H
|
||||
#define LIBSVG_POLYGON_H
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
class polygon : public shape {
|
||||
private:
|
||||
std::string points;
|
||||
|
||||
public:
|
||||
polygon();
|
||||
polygon(const polygon& orig);
|
||||
virtual ~polygon();
|
||||
|
||||
virtual void set_attrs(attr_map_t& attrs);
|
||||
const std::string& get_name() const { return polygon::name; };
|
||||
|
||||
static const std::string name;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LIBSVG_POLYGON_H */
|
||||
|
||||
50
src/libsvg/polyline.cc
Normal file
50
src/libsvg/polyline.cc
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#include <boost/tokenizer.hpp>
|
||||
|
||||
#include "polyline.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
const std::string polyline::name("polyline");
|
||||
|
||||
polyline::polyline()
|
||||
{
|
||||
}
|
||||
|
||||
polyline::polyline(const polyline& orig) : shape(orig)
|
||||
{
|
||||
points = orig.points;
|
||||
}
|
||||
|
||||
polyline::~polyline()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
polyline::set_attrs(attr_map_t& attrs)
|
||||
{
|
||||
shape::set_attrs(attrs);
|
||||
this->points = attrs["points"];
|
||||
|
||||
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
|
||||
boost::char_separator<char> sep(" ,");
|
||||
tokenizer tokens(this->points, sep);
|
||||
|
||||
double x = 0.0;
|
||||
path_t path;
|
||||
bool first = true;
|
||||
for (tokenizer::iterator it = tokens.begin();it != tokens.end();++it) {
|
||||
std::string v = (*it);
|
||||
double p = parse_double(v);
|
||||
|
||||
if (first) {
|
||||
x = p;
|
||||
} else {
|
||||
path.push_back(Eigen::Vector3d(x, p, 0));
|
||||
}
|
||||
first = !first;
|
||||
}
|
||||
|
||||
offset_path(path_list, path, get_stroke_width(), get_stroke_linecap());
|
||||
}
|
||||
|
||||
}
|
||||
26
src/libsvg/polyline.h
Normal file
26
src/libsvg/polyline.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef LIBSVG_POLYLINE_H
|
||||
#define LIBSVG_POLYLINE_H
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
class polyline : public shape {
|
||||
private:
|
||||
std::string points;
|
||||
|
||||
public:
|
||||
polyline();
|
||||
polyline(const polyline& orig);
|
||||
virtual ~polyline();
|
||||
|
||||
virtual void set_attrs(attr_map_t& attrs);
|
||||
const std::string& get_name() const { return polyline::name; };
|
||||
|
||||
static const std::string name;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LIBSVG_POLYLINE_H */
|
||||
|
||||
150
src/libsvg/rect.cc
Normal file
150
src/libsvg/rect.cc
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include "rect.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
const std::string rect::name("rect");
|
||||
|
||||
rect::rect()
|
||||
{
|
||||
}
|
||||
|
||||
rect::rect(const rect& orig) : path(orig)
|
||||
{
|
||||
rx = orig.rx;
|
||||
ry = orig.ry;
|
||||
width = orig.width;
|
||||
height = orig.height;
|
||||
}
|
||||
|
||||
rect::~rect()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Let rx and ry be length values.
|
||||
*
|
||||
* If neither ‘rx’ nor ‘ry’ are properly specified, then set both rx and
|
||||
* ry to 0. (This will result in square corners.)
|
||||
* Otherwise, if a properly specified value is provided for ‘rx’, but not
|
||||
* for ‘ry’, then set both rx and ry to the value of ‘rx’.
|
||||
* Otherwise, if a properly specified value is provided for ‘ry’, but not
|
||||
* for ‘rx’, then set both rx and ry to the value of ‘ry’.
|
||||
* Otherwise, both ‘rx’ and ‘ry’ were specified properly. Set rx to the
|
||||
* value of ‘rx’ and ry to the value of ‘ry’.
|
||||
* If rx is greater than half of ‘width’, then set rx to half of ‘width’.
|
||||
* If ry is greater than half of ‘height’, then set ry to half of ‘height’.
|
||||
* The effective values of ‘rx’ and ‘ry’ are rx and ry, respectively.
|
||||
*
|
||||
*
|
||||
* Mathematically, a ‘rect’ element can be mapped to an equivalent
|
||||
* ‘path’ element as follows: (Note: all coordinate and length values
|
||||
* are first converted into user space coordinates according to Units.)
|
||||
*
|
||||
* 1) perform an absolute moveto operation to location (x+rx,y), where x is
|
||||
* the value of the ‘rect’ element's ‘x’ attribute converted to user space,
|
||||
* rx is the effective value of the ‘rx’ attribute converted to user space
|
||||
* and y is the value of the ‘y’ attribute converted to user space
|
||||
*
|
||||
* 2) perform an absolute horizontal lineto operation to location (x+width-rx,y),
|
||||
* where width is the ‘rect’ element's ‘width’ attribute converted to user
|
||||
* space
|
||||
*
|
||||
* 3) perform an absolute elliptical arc operation to coordinate (x+width,y+ry),
|
||||
* where the effective values for the ‘rx’ and ‘ry’ attributes on the ‘rect’
|
||||
* element converted to user space are used as the rx and ry attributes on
|
||||
* the elliptical arc command, respectively, the x-axis-rotation is set to
|
||||
* zero, the large-arc-flag is set to zero, and the sweep-flag is set to
|
||||
* one
|
||||
*
|
||||
* 4) perform a absolute vertical lineto to location (x+width,y+height-ry),
|
||||
* where height is the ‘rect’ element's ‘height’ attribute converted to user
|
||||
* space
|
||||
*
|
||||
* 5) perform an absolute elliptical arc operation to coordinate (x+width-rx,y+height)
|
||||
*
|
||||
* 6) perform an absolute horizontal lineto to location (x+rx,y+height)
|
||||
*
|
||||
* 7) perform an absolute elliptical arc operation to coordinate (x,y+height-ry)
|
||||
*
|
||||
* 8) perform an absolute absolute vertical lineto to location (x,y+ry)
|
||||
*
|
||||
* 9) perform an absolute elliptical arc operation to coordinate (x+rx,y)
|
||||
*/
|
||||
void
|
||||
rect::set_attrs(attr_map_t& attrs)
|
||||
{
|
||||
shape::set_attrs(attrs);
|
||||
this->x = parse_double(attrs["x"]);
|
||||
this->y = parse_double(attrs["y"]);
|
||||
this->width = parse_double(attrs["width"]);
|
||||
this->height = parse_double(attrs["height"]);
|
||||
this->rx = parse_double(attrs["rx"]);
|
||||
this->ry = parse_double(attrs["ry"]);
|
||||
|
||||
bool has_rx = !(abs(rx) < 1e-8);
|
||||
bool has_ry = !(abs(ry) < 1e-8);
|
||||
|
||||
if (has_rx || has_ry) {
|
||||
if (!has_rx) {
|
||||
this->rx = this->ry;
|
||||
} else if (!has_ry) {
|
||||
this->ry = this->rx;
|
||||
}
|
||||
if (this->rx > (this->width / 2)) {
|
||||
this->rx = this->width / 2;
|
||||
}
|
||||
if (this->ry > (this->height / 2)) {
|
||||
this->ry = this->height / 2;
|
||||
}
|
||||
|
||||
std::string path = boost::str(boost::format(""
|
||||
"M %1%,%2% "
|
||||
"H %3% "
|
||||
"A %4%,%5% 0 0,1 %6%,%7% "
|
||||
"V %8% "
|
||||
"A %9%,%10% 0 0,1 %11%,%12% "
|
||||
"H %13% "
|
||||
"A %14%,%15% 0 0,1 %16%,%17% "
|
||||
"V %18% "
|
||||
"A %19%,%20% 0 0,1 %21%,%22% "
|
||||
"z")
|
||||
% (x + rx) % y
|
||||
% (x + width - rx)
|
||||
% rx % ry % (x + width) % (y + ry)
|
||||
% (y + height - ry)
|
||||
% rx % ry % (x + width - rx) % (y + height)
|
||||
% (x + rx)
|
||||
% rx % ry % x % (y + height - ry)
|
||||
% (y + ry)
|
||||
% rx % ry % (x + rx) % y
|
||||
);
|
||||
attrs["d"] = path;
|
||||
path::set_attrs(attrs);
|
||||
} else {
|
||||
path_t path;
|
||||
path.push_back(Eigen::Vector3d(get_x(), get_y(), 0));
|
||||
path.push_back(Eigen::Vector3d(get_x() + get_width(), get_y(), 0));
|
||||
path.push_back(Eigen::Vector3d(get_x() + get_width(), get_y() + get_height(), 0));
|
||||
path.push_back(Eigen::Vector3d(get_x(), get_y() + get_height(), 0));
|
||||
path.push_back(Eigen::Vector3d(get_x(), get_y(), 0));
|
||||
path_list.push_back(path);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rect::dump()
|
||||
{
|
||||
std::cout << get_name()
|
||||
<< ": x = " << this->x
|
||||
<< ": y = " << this->y
|
||||
<< ": width = " << this->width
|
||||
<< ": height = " << this->height
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
36
src/libsvg/rect.h
Normal file
36
src/libsvg/rect.h
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#ifndef LIBSVG_RECT_H
|
||||
#define LIBSVG_RECT_H
|
||||
|
||||
#include "path.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
class rect : public path
|
||||
{
|
||||
protected:
|
||||
double width;
|
||||
double height;
|
||||
double rx;
|
||||
double ry;
|
||||
|
||||
public:
|
||||
rect();
|
||||
rect(const rect& orig);
|
||||
virtual ~rect();
|
||||
|
||||
virtual double get_width() { return width; }
|
||||
virtual double get_height() { return height; }
|
||||
virtual double get_rx() { return rx; }
|
||||
virtual double get_ry() { return ry; }
|
||||
|
||||
virtual void set_attrs(attr_map_t& attrs);
|
||||
virtual void dump();
|
||||
const std::string& get_name() const { return rect::name; };
|
||||
|
||||
static const std::string name;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LIBSVG_RECT_H */
|
||||
|
||||
256
src/libsvg/shape.cc
Normal file
256
src/libsvg/shape.cc
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
#include "shape.h"
|
||||
#include "circle.h"
|
||||
#include "ellipse.h"
|
||||
#include "line.h"
|
||||
#include "polygon.h"
|
||||
#include "polyline.h"
|
||||
#include "rect.h"
|
||||
#include "svgpage.h"
|
||||
#include "path.h"
|
||||
#include "group.h"
|
||||
|
||||
#include "transformation.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
shape::shape() : parent(NULL), x(0), y(0)
|
||||
{
|
||||
}
|
||||
|
||||
shape::shape(const shape& orig)
|
||||
{
|
||||
}
|
||||
|
||||
shape::~shape()
|
||||
{
|
||||
}
|
||||
|
||||
shape *
|
||||
shape::create_from_name(const char *name)
|
||||
{
|
||||
if (circle::name == name) {
|
||||
return new circle();
|
||||
} else if (ellipse::name == name) {
|
||||
return new ellipse();
|
||||
} else if (line::name == name) {
|
||||
return new line();
|
||||
} else if (polygon::name == name) {
|
||||
return new polygon();
|
||||
} else if (polyline::name == name) {
|
||||
return new polyline();
|
||||
} else if (rect::name == name) {
|
||||
return new rect();
|
||||
} else if (svgpage::name == name) {
|
||||
return new svgpage();
|
||||
} else if (path::name == name) {
|
||||
return new path();
|
||||
} else if (group::name == name) {
|
||||
return new group();
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
shape::set_attrs(attr_map_t& attrs)
|
||||
{
|
||||
this->id = attrs["id"];
|
||||
this->transform = attrs["transform"];
|
||||
this->stroke_width = attrs["stroke-width"];
|
||||
this->stroke_linecap = attrs["stroke-linecap"];
|
||||
this->style = attrs["style"];
|
||||
}
|
||||
|
||||
std::string
|
||||
shape::get_style(std::string name)
|
||||
{
|
||||
std::vector<std::string> styles;
|
||||
boost::split(styles, this->style, boost::is_any_of(";"));
|
||||
|
||||
for (std::vector<std::string>::iterator it = styles.begin();it != styles.end();it++) {
|
||||
std::vector<std::string> values;
|
||||
boost::split(values, *it, boost::is_any_of(":"));
|
||||
if (values.size() != 2) {
|
||||
continue;
|
||||
}
|
||||
if (name == values[0]) {
|
||||
return values[1];
|
||||
}
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
double
|
||||
shape::get_stroke_width()
|
||||
{
|
||||
double stroke_width;
|
||||
if (this->stroke_width.empty()) {
|
||||
stroke_width = parse_double(get_style("stroke-width"));
|
||||
} else {
|
||||
stroke_width = parse_double(this->stroke_width);
|
||||
}
|
||||
return stroke_width < 0.01 ? 1 : stroke_width;
|
||||
}
|
||||
|
||||
ClipperLib::EndType
|
||||
shape::get_stroke_linecap()
|
||||
{
|
||||
std::string cap;
|
||||
if (this->stroke_linecap.empty()) {
|
||||
cap = get_style("stroke-linecap");
|
||||
} else {
|
||||
cap = this->stroke_linecap;
|
||||
}
|
||||
|
||||
if (cap == "butt") {
|
||||
return ClipperLib::etOpenButt;
|
||||
} else if (cap == "round") {
|
||||
return ClipperLib::etOpenRound;
|
||||
} else if (cap == "square") {
|
||||
return ClipperLib::etOpenSquare;
|
||||
}
|
||||
return ClipperLib::etOpenSquare;
|
||||
}
|
||||
|
||||
void
|
||||
shape::collect_transform_matrices(std::vector<Eigen::Matrix3d>& matrices, shape *s)
|
||||
{
|
||||
std::string transform_arg(s->transform);
|
||||
|
||||
boost::replace_all(transform_arg, "matrix", "m");
|
||||
boost::replace_all(transform_arg, "translate", "t");
|
||||
boost::replace_all(transform_arg, "scale", "s");
|
||||
boost::replace_all(transform_arg, "rotate", "r");
|
||||
boost::replace_all(transform_arg, "skewX", "x");
|
||||
boost::replace_all(transform_arg, "skewY", "y");
|
||||
|
||||
std::string commands = "mtsrxy";
|
||||
|
||||
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
|
||||
boost::char_separator<char> sep(" ,()", commands.c_str());
|
||||
tokenizer tokens(transform_arg, sep);
|
||||
|
||||
transformation *t = NULL;
|
||||
std::vector<transformation *> transformations;
|
||||
for (tokenizer::iterator it = tokens.begin();it != tokens.end();++it) {
|
||||
std::string v = (*it);
|
||||
if ((v.length() == 1) && (commands.find(v) != std::string::npos)) {
|
||||
if (t != NULL) {
|
||||
transformations.push_back(t);
|
||||
t = NULL;
|
||||
}
|
||||
switch (v[0]) {
|
||||
case 'm':
|
||||
t = new matrix();
|
||||
break;
|
||||
case 't':
|
||||
t = new translate();
|
||||
break;
|
||||
case 's':
|
||||
t = new scale();
|
||||
break;
|
||||
case 'r':
|
||||
t = new rotate();
|
||||
break;
|
||||
case 'x':
|
||||
t = new skew_x();
|
||||
break;
|
||||
case 'y':
|
||||
t = new skew_y();
|
||||
break;
|
||||
default:
|
||||
std::cout << "unknown transform op " << v << std::endl;
|
||||
t = NULL;
|
||||
}
|
||||
} else {
|
||||
if (t) {
|
||||
t->add_arg(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (t != NULL) {
|
||||
transformations.push_back(t);
|
||||
}
|
||||
|
||||
for (std::vector<transformation *>::reverse_iterator it = transformations.rbegin();it != transformations.rend();it++) {
|
||||
transformation *t = *it;
|
||||
std::vector<Eigen::Matrix3d> m = t->get_matrices();
|
||||
matrices.insert(matrices.begin(), m.rbegin(), m.rend());
|
||||
delete t;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
shape::apply_transform()
|
||||
{
|
||||
std::vector<Eigen::Matrix3d> matrices;
|
||||
for (shape *s = this;s->get_parent() != NULL;s = s->get_parent()) {
|
||||
collect_transform_matrices(matrices, s);
|
||||
}
|
||||
|
||||
path_list_t result_list;
|
||||
for (path_list_t::iterator it = path_list.begin();it != path_list.end();it++) {
|
||||
path_t& p = *it;
|
||||
|
||||
result_list.push_back(path_t());
|
||||
for (path_t::iterator it2 = p.begin();it2 != p.end();it2++) {
|
||||
Eigen::Vector3d result((*it2).x(), (*it2).y(), 1);
|
||||
for (std::vector<Eigen::Matrix3d>::reverse_iterator it3 = matrices.rbegin();it3 != matrices.rend();it3++) {
|
||||
result = *it3 * result;
|
||||
}
|
||||
|
||||
result_list.back().push_back(result);
|
||||
}
|
||||
}
|
||||
path_list = result_list;
|
||||
}
|
||||
|
||||
void
|
||||
shape::offset_path(path_list_t& path_list, path_t& path, double stroke_width, ClipperLib::EndType stroke_linecap) {
|
||||
ClipperLib::Path line;
|
||||
ClipperLib::Paths result;
|
||||
for (path_t::iterator it = path.begin();it != path.end();it++) {
|
||||
Eigen::Vector3d& v = *it;
|
||||
line << ClipperLib::IntPoint(v.x() * 10000, v.y() * 10000);
|
||||
}
|
||||
|
||||
ClipperLib::ClipperOffset co;
|
||||
co.AddPath(line, ClipperLib::jtMiter, stroke_linecap);
|
||||
co.Execute(result, stroke_width * 5000.0);
|
||||
|
||||
for (ClipperLib::Paths::iterator it = result.begin();it != result.end();it++) {
|
||||
ClipperLib::Path& p = *it;
|
||||
path_list.push_back(path_t());
|
||||
for (ClipperLib::Path::iterator it2 = p.begin();it2 != p.end();it2++) {
|
||||
ClipperLib::IntPoint& point = *it2;
|
||||
path_list.back().push_back(Eigen::Vector3d(point.X / 10000.0, point.Y / 10000.0, 0));
|
||||
}
|
||||
path_list.back().push_back(Eigen::Vector3d(p[0].X / 10000.0, p[0].Y / 10000.0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
shape::draw_ellipse(path_t& path, double x, double y, double rx, double ry) {
|
||||
unsigned long fn = 40;
|
||||
for (unsigned long idx = 1;idx <= fn;idx++) {
|
||||
const double a = idx * (2 * M_PI / (double)fn);
|
||||
const double xx = rx * sin(a) + x;
|
||||
const double yy = ry * cos(a) + y;
|
||||
path.push_back(Eigen::Vector3d(xx, yy, 0));
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream &os, const shape& s)
|
||||
{
|
||||
return os << s.get_name() << " | id = '" << s.id << "', transform = '" << s.transform << "'";
|
||||
}
|
||||
|
||||
}
|
||||
77
src/libsvg/shape.h
Normal file
77
src/libsvg/shape.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
#ifndef LIBSVG_SHAPE_H
|
||||
#define LIBSVG_SHAPE_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
#include "util.h"
|
||||
#include "polyclipping/clipper.hpp"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
typedef std::vector<Eigen::Vector3d> path_t;
|
||||
typedef std::vector<path_t> path_list_t;
|
||||
typedef std::map<std::string, std::string> attr_map_t;
|
||||
|
||||
class shape {
|
||||
private:
|
||||
shape *parent;
|
||||
std::vector<shape *> children;
|
||||
|
||||
protected:
|
||||
std::string id;
|
||||
double x;
|
||||
double y;
|
||||
path_list_t path_list;
|
||||
std::string transform;
|
||||
std::string stroke_width;
|
||||
std::string stroke_linecap;
|
||||
std::string style;
|
||||
|
||||
double get_stroke_width();
|
||||
ClipperLib::EndType get_stroke_linecap();
|
||||
std::string get_style(std::string name);
|
||||
void draw_ellipse(path_t& path, double x, double y, double rx, double ry);
|
||||
void offset_path(path_list_t& path_list, path_t& path, double stroke_width, ClipperLib::EndType stroke_linecap);
|
||||
void collect_transform_matrices(std::vector<Eigen::Matrix3d>& matrices, shape *s);
|
||||
|
||||
public:
|
||||
shape();
|
||||
shape(const shape& orig);
|
||||
virtual ~shape();
|
||||
|
||||
virtual shape * get_parent() { return parent; }
|
||||
virtual void set_parent(shape *s) { parent = s; }
|
||||
virtual void add_child(shape *s) { children.push_back(s); s->set_parent(this); }
|
||||
virtual std::vector<shape *>& get_children() { return children; }
|
||||
|
||||
virtual std::string& get_id() { return id; }
|
||||
virtual double get_x() { return x; }
|
||||
virtual double get_y() { return y; }
|
||||
|
||||
virtual path_list_t& get_path_list() { return path_list; }
|
||||
|
||||
virtual bool is_container() { return false; }
|
||||
|
||||
virtual void apply_transform();
|
||||
|
||||
virtual const std::string& get_name() const = 0;
|
||||
virtual void set_attrs(attr_map_t& attrs);
|
||||
virtual void dump() {}
|
||||
|
||||
static shape * create_from_name(const char *name);
|
||||
|
||||
private:
|
||||
friend std::ostream & operator<<(std::ostream &os, const shape& s);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LIBSVG_SHAPE_H */
|
||||
|
||||
44
src/libsvg/svgpage.cc
Normal file
44
src/libsvg/svgpage.cc
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "svgpage.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
const std::string svgpage::name("svg");
|
||||
|
||||
svgpage::svgpage()
|
||||
{
|
||||
}
|
||||
|
||||
svgpage::svgpage(const svgpage& orig) : shape(orig)
|
||||
{
|
||||
width = orig.width;
|
||||
height = orig.height;
|
||||
}
|
||||
|
||||
svgpage::~svgpage()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
svgpage::set_attrs(attr_map_t& attrs)
|
||||
{
|
||||
this->x = 0;
|
||||
this->y = 0;
|
||||
this->width = parse_double(attrs["width"]);
|
||||
this->height = parse_double(attrs["height"]);
|
||||
}
|
||||
|
||||
void
|
||||
svgpage::dump()
|
||||
{
|
||||
std::cout << get_name()
|
||||
<< ": x = " << this->x
|
||||
<< ": y = " << this->y
|
||||
<< ": width = " << this->width
|
||||
<< ": height = " << this->height
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
31
src/libsvg/svgpage.h
Normal file
31
src/libsvg/svgpage.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#ifndef LIBSVG_SVGPAGE_H
|
||||
#define LIBSVG_SVGPAGE_H
|
||||
|
||||
#include "shape.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
class svgpage : public shape {
|
||||
protected:
|
||||
double width;
|
||||
double height;
|
||||
|
||||
public:
|
||||
svgpage();
|
||||
svgpage(const svgpage& orig);
|
||||
virtual ~svgpage();
|
||||
|
||||
virtual double get_width() { return width; }
|
||||
virtual double get_height() { return height; }
|
||||
virtual bool is_container() { return true; }
|
||||
|
||||
virtual void set_attrs(attr_map_t& attrs);
|
||||
virtual void dump();
|
||||
const std::string& get_name() const { return svgpage::name; };
|
||||
|
||||
static const std::string name;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LIBSVG_SVGPAGE_H */
|
||||
274
src/libsvg/transformation.cc
Normal file
274
src/libsvg/transformation.cc
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
#include "util.h"
|
||||
#include "transformation.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
transformation::transformation(const std::string op, const std::string name) : op(op), name(name)
|
||||
{
|
||||
}
|
||||
|
||||
transformation::~transformation()
|
||||
{
|
||||
}
|
||||
|
||||
const std::string&
|
||||
transformation::get_op()
|
||||
{
|
||||
return op;
|
||||
}
|
||||
|
||||
const std::string&
|
||||
transformation::get_name()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
void
|
||||
transformation::add_arg(const std::string arg)
|
||||
{
|
||||
double d = parse_double(arg);
|
||||
args.push_back(d);
|
||||
}
|
||||
|
||||
const std::string
|
||||
transformation::get_args() {
|
||||
std::stringstream str;
|
||||
for (int a = 0;a < args.size();a++) {
|
||||
str << ((a == 0) ? "(" : ", ") << args[a];
|
||||
}
|
||||
str << ")";
|
||||
return str.str();
|
||||
}
|
||||
|
||||
matrix::matrix() : transformation("m", "matrix")
|
||||
{
|
||||
}
|
||||
|
||||
matrix::~matrix()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* matrix(<a> <b> <c> <d> <e> <f>), which specifies a transformation in
|
||||
* the form of a transformation matrix of six values. matrix(a,b,c,d,e,f)
|
||||
* is equivalent to applying the transformation matrix [a b c d e f].
|
||||
*/
|
||||
std::vector<Eigen::Matrix3d>
|
||||
matrix::get_matrices()
|
||||
{
|
||||
if (args.size() != 6) {
|
||||
std::cout << "invalid arguments for matrix" << std::endl;
|
||||
return std::vector<Eigen::Matrix3d>();
|
||||
}
|
||||
|
||||
Eigen::Matrix3d m;
|
||||
m <<
|
||||
args[0], args[2], args[4],
|
||||
args[1], args[3], args[5],
|
||||
0, 0, 1;
|
||||
|
||||
std::vector<Eigen::Matrix3d> result;
|
||||
result.push_back(m);
|
||||
return result;
|
||||
}
|
||||
|
||||
translate::translate() : transformation("t", "translate")
|
||||
{
|
||||
}
|
||||
|
||||
translate::~translate()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* translate(<tx> [<ty>]), which specifies a translation by tx and ty.
|
||||
* If <ty> is not provided, it is assumed to be zero.
|
||||
*/
|
||||
std::vector<Eigen::Matrix3d>
|
||||
translate::get_matrices()
|
||||
{
|
||||
if ((args.size() < 1) || (args.size() > 2)) {
|
||||
std::cout << "invalid arguments for " << get_name() << std::endl;
|
||||
return std::vector<Eigen::Matrix3d>();
|
||||
}
|
||||
|
||||
double tx = args[0];
|
||||
double ty = args.size() > 1 ? args[1] : 0;
|
||||
|
||||
Eigen::Matrix3d m;
|
||||
m <<
|
||||
1, 0, tx,
|
||||
0, 1, ty,
|
||||
0, 0, 1;
|
||||
|
||||
std::vector<Eigen::Matrix3d> result;
|
||||
result.push_back(m);
|
||||
return result;
|
||||
}
|
||||
|
||||
scale::scale() : transformation("s", "scale")
|
||||
{
|
||||
}
|
||||
|
||||
scale::~scale()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* scale(<sx> [<sy>]), which specifies a scale operation by sx and sy.
|
||||
* If <sy> is not provided, it is assumed to be equal to <sx>.
|
||||
*/
|
||||
std::vector<Eigen::Matrix3d>
|
||||
scale::get_matrices()
|
||||
{
|
||||
if ((args.size() < 1) || (args.size() > 2)) {
|
||||
std::cout << "invalid arguments for " << get_name() << std::endl;
|
||||
return std::vector<Eigen::Matrix3d>();
|
||||
}
|
||||
|
||||
double sx = args[0];
|
||||
double sy = args.size() > 1 ? args[1] : args[0];
|
||||
|
||||
Eigen::Matrix3d m;
|
||||
m <<
|
||||
sx, 0, 0,
|
||||
0, sy, 0,
|
||||
0, 0, 1;
|
||||
|
||||
std::vector<Eigen::Matrix3d> result;
|
||||
result.push_back(m);
|
||||
return result;
|
||||
}
|
||||
|
||||
rotate::rotate() : transformation("r", "rotate")
|
||||
{
|
||||
}
|
||||
|
||||
rotate::~rotate()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* rotate(<rotate-angle> [<cx> <cy>]), which specifies a rotation by
|
||||
* <rotate-angle> degrees about a given point.
|
||||
* If optional parameters <cx> and <cy> are not supplied, the rotate is
|
||||
* about the origin of the current user coordinate system. The operation
|
||||
* corresponds to the matrix [cos(a) sin(a) -sin(a) cos(a) 0 0].
|
||||
* If optional parameters <cx> and <cy> are supplied, the rotate is
|
||||
* about the point (cx, cy). The operation represents the equivalent of
|
||||
* the following specification: translate(<cx>, <cy>) rotate(<rotate-angle>)
|
||||
* translate(-<cx>, -<cy>).
|
||||
*/
|
||||
std::vector<Eigen::Matrix3d>
|
||||
rotate::get_matrices()
|
||||
{
|
||||
if ((args.size() != 1) && (args.size() != 3)) {
|
||||
std::cout << "invalid arguments for " << get_name() << std::endl;
|
||||
return std::vector<Eigen::Matrix3d>();
|
||||
}
|
||||
|
||||
bool has_center = args.size() == 3;
|
||||
|
||||
double angle = M_PI * args[0] / 180.0;
|
||||
double cx = has_center ? args[1] : 0;
|
||||
double cy = has_center ? args[2] : 0;
|
||||
|
||||
std::vector<Eigen::Matrix3d> result;
|
||||
|
||||
if (has_center) {
|
||||
Eigen::Matrix3d t;
|
||||
t <<
|
||||
1, 0, -cx,
|
||||
0, 1, -cy,
|
||||
0, 0, 1;
|
||||
result.push_back(t);
|
||||
}
|
||||
|
||||
Eigen::Matrix3d m;
|
||||
m <<
|
||||
cos(angle), -sin(angle), 0,
|
||||
sin(angle), cos(angle), 0,
|
||||
0, 0, 1;
|
||||
result.push_back(m);
|
||||
|
||||
if (has_center) {
|
||||
Eigen::Matrix3d t;
|
||||
t <<
|
||||
1, 0, cx,
|
||||
0, 1, cy,
|
||||
0, 0, 1;
|
||||
result.push_back(t);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
skew_x::skew_x() : transformation("x", "skew_x")
|
||||
{
|
||||
}
|
||||
|
||||
skew_x::~skew_x()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* skewX(<skew-angle>), which specifies a skew transformation along the x-axis.
|
||||
*/
|
||||
std::vector<Eigen::Matrix3d>
|
||||
skew_x::get_matrices()
|
||||
{
|
||||
if (args.size() != 1) {
|
||||
std::cout << "invalid arguments for " << get_name() << std::endl;
|
||||
return std::vector<Eigen::Matrix3d>();
|
||||
}
|
||||
|
||||
double angle = M_PI * args[0] / 180.0;
|
||||
|
||||
Eigen::Matrix3d m;
|
||||
m <<
|
||||
1, tan(angle), 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1;
|
||||
|
||||
std::vector<Eigen::Matrix3d> result;
|
||||
result.push_back(m);
|
||||
return result;
|
||||
}
|
||||
|
||||
skew_y::skew_y() : transformation("y", "skew_y")
|
||||
{
|
||||
}
|
||||
|
||||
skew_y::~skew_y()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* skewY(<skew-angle>), which specifies a skew transformation along the y-axis.
|
||||
*/
|
||||
std::vector<Eigen::Matrix3d>
|
||||
skew_y::get_matrices()
|
||||
{
|
||||
if (args.size() != 1) {
|
||||
std::cout << "invalid arguments for " << get_name() << std::endl;
|
||||
return std::vector<Eigen::Matrix3d>();
|
||||
}
|
||||
|
||||
double angle = M_PI * args[0] / 180.0;
|
||||
|
||||
Eigen::Matrix3d m;
|
||||
m <<
|
||||
1, 0, 0,
|
||||
tan(angle), 1, 0,
|
||||
0, 0, 1;
|
||||
|
||||
std::vector<Eigen::Matrix3d> result;
|
||||
result.push_back(m);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
81
src/libsvg/transformation.h
Normal file
81
src/libsvg/transformation.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#ifndef LIBSVG_TRANSFORM_H
|
||||
#define LIBSVG_TRANSFORM_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <Eigen/Core>
|
||||
#include <Eigen/Geometry>
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
class transformation {
|
||||
private:
|
||||
const std::string op;
|
||||
const std::string name;
|
||||
protected:
|
||||
std::vector<double> args;
|
||||
|
||||
public:
|
||||
transformation(const std::string op, const std::string name);
|
||||
virtual ~transformation();
|
||||
|
||||
virtual const std::string& get_op();
|
||||
virtual const std::string& get_name();
|
||||
virtual const std::string get_args();
|
||||
|
||||
virtual void add_arg(const std::string arg);
|
||||
virtual std::vector<Eigen::Matrix3d> get_matrices() = 0;
|
||||
};
|
||||
|
||||
class matrix : public transformation {
|
||||
public:
|
||||
matrix();
|
||||
virtual ~matrix();
|
||||
|
||||
virtual std::vector<Eigen::Matrix3d> get_matrices();
|
||||
};
|
||||
|
||||
class translate : public transformation {
|
||||
public:
|
||||
translate();
|
||||
virtual ~translate();
|
||||
|
||||
virtual std::vector<Eigen::Matrix3d> get_matrices();
|
||||
};
|
||||
|
||||
class scale : public transformation {
|
||||
public:
|
||||
scale();
|
||||
virtual ~scale();
|
||||
|
||||
virtual std::vector<Eigen::Matrix3d> get_matrices();
|
||||
};
|
||||
|
||||
class rotate : public transformation {
|
||||
public:
|
||||
rotate();
|
||||
virtual ~rotate();
|
||||
|
||||
virtual std::vector<Eigen::Matrix3d> get_matrices();
|
||||
};
|
||||
|
||||
class skew_x : public transformation {
|
||||
public:
|
||||
skew_x();
|
||||
virtual ~skew_x();
|
||||
|
||||
virtual std::vector<Eigen::Matrix3d> get_matrices();
|
||||
};
|
||||
|
||||
class skew_y : public transformation {
|
||||
public:
|
||||
skew_y();
|
||||
virtual ~skew_y();
|
||||
|
||||
virtual std::vector<Eigen::Matrix3d> get_matrices();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* LIBSVG_TRANSFORM_H */
|
||||
27
src/libsvg/util.cc
Normal file
27
src/libsvg/util.cc
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
double
|
||||
parse_double(const std::string& number)
|
||||
{
|
||||
std::string::const_iterator iter = number.begin(), end = number.end();
|
||||
|
||||
boost::spirit::qi::real_parser<double, boost::spirit::qi::real_policies<double> > double_parser;
|
||||
|
||||
double d = 0.0;
|
||||
|
||||
bool result = boost::spirit::qi::parse(iter, end, double_parser, d);
|
||||
if(result && iter == end)
|
||||
{
|
||||
return d;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
8
src/libsvg/util.h
Normal file
8
src/libsvg/util.h
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
#include <string>
|
||||
|
||||
namespace libsvg {
|
||||
|
||||
double
|
||||
parse_double(const std::string& number);
|
||||
|
||||
}
|
||||
|
|
@ -248,6 +248,7 @@ MainWindow::MainWindow(const QString &filename)
|
|||
knownFileExtensions["stl"] = importStatement;
|
||||
knownFileExtensions["off"] = importStatement;
|
||||
knownFileExtensions["dxf"] = importStatement;
|
||||
if (Feature::ExperimentalSvgImport.is_enabled()) knownFileExtensions["svg"] = importStatement;
|
||||
knownFileExtensions["dat"] = surfaceStatement;
|
||||
knownFileExtensions["png"] = surfaceStatement;
|
||||
knownFileExtensions["scad"] = "";
|
||||
|
|
|
|||
|
|
@ -551,6 +551,10 @@ add_definitions(${FONTCONFIG_CFLAGS})
|
|||
add_definitions(${FREETYPE_CFLAGS})
|
||||
add_definitions(${HARFBUZZ_CFLAGS})
|
||||
|
||||
find_package(LibXml2 2.9 REQUIRED)
|
||||
add_definitions(${LIBXML2_DEFINITIONS})
|
||||
inclusion(LIBXML2_DIR LIBXML2_INCLUDE_DIR)
|
||||
|
||||
# Image comparison - expected test image vs actual generated image
|
||||
|
||||
if (DIFFPNG)
|
||||
|
|
@ -562,6 +566,8 @@ if (DIFFPNG)
|
|||
message(STATUS "using diffpng for image comparison")
|
||||
endif()
|
||||
|
||||
# Imagemagick
|
||||
|
||||
if (SKIP_IMAGEMAGICK)
|
||||
if (NOT DIFFPNG)
|
||||
# cross-building depends on this
|
||||
|
|
@ -720,6 +726,21 @@ set(CORE_SOURCES
|
|||
../src/FreetypeRenderer.cc
|
||||
../src/lodepng.cpp
|
||||
../src/PlatformUtils.cc
|
||||
../src/libsvg/circle.cc
|
||||
../src/libsvg/ellipse.cc
|
||||
../src/libsvg/group.cc
|
||||
../src/libsvg/libsvg.cc
|
||||
../src/libsvg/line.cc
|
||||
../src/libsvg/path.cc
|
||||
../src/libsvg/polygon.cc
|
||||
../src/libsvg/polyline.cc
|
||||
../src/libsvg/rect.cc
|
||||
../src/libsvg/shape.cc
|
||||
../src/libsvg/svgpage.cc
|
||||
../src/libsvg/transformation.cc
|
||||
../src/libsvg/util.cc
|
||||
../src/clipper-utils.cc
|
||||
../src/polyclipping/clipper.cpp
|
||||
../src/${PLATFORMUTILS_SOURCE}
|
||||
${FLEX_OpenSCADlexer_OUTPUTS}
|
||||
${BISON_OpenSCADparser_OUTPUTS})
|
||||
|
|
@ -729,6 +750,7 @@ set(NOCGAL_SOURCES
|
|||
../src/import.cc
|
||||
../src/import_stl.cc
|
||||
../src/import_off.cc
|
||||
../src/import_svg.cc
|
||||
../src/export.cc
|
||||
../src/export_stl.cc
|
||||
../src/export_amf.cc
|
||||
|
|
@ -769,7 +791,8 @@ set(COMMON_SOURCES
|
|||
../src/libtess2/Source/mesh.c
|
||||
../src/libtess2/Source/priorityq.c
|
||||
../src/libtess2/Source/sweep.c
|
||||
../src/libtess2/Source/tess.c)
|
||||
../src/libtess2/Source/tess.c
|
||||
../src/Tree.cc)
|
||||
|
||||
#
|
||||
# Offscreen OpenGL context source code
|
||||
|
|
@ -805,7 +828,7 @@ if(NULLGL)
|
|||
endif()
|
||||
|
||||
add_library(tests-core STATIC ${CORE_SOURCES})
|
||||
target_link_libraries(tests-core ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} ${FONTCONFIG_LDFLAGS} ${FREETYPE_LDFLAGS} ${HARFBUZZ_LDFLAGS} ${Boost_LIBRARIES} ${COCOA_LIBRARY})
|
||||
target_link_libraries(tests-core ${OPENGL_LIBRARIES} ${GLIB2_LIBRARIES} ${FONTCONFIG_LDFLAGS} ${FREETYPE_LDFLAGS} ${HARFBUZZ_LDFLAGS} ${LIBXML2_LIBRARIES} ${Boost_LIBRARIES} ${COCOA_LIBRARY})
|
||||
|
||||
add_library(tests-common STATIC ${COMMON_SOURCES})
|
||||
target_link_libraries(tests-common tests-core)
|
||||
|
|
@ -1257,6 +1280,9 @@ disable_tests(
|
|||
dxfpngtest_text-empty-tests
|
||||
dxfpngtest_nothing-decimal-comma-separated
|
||||
dxfpngtest_nullspace-2d
|
||||
svgpngtest_text-empty-tests
|
||||
svgpngtest_nothing-decimal-comma-separated
|
||||
svgpngtest_nullspace-2d
|
||||
|
||||
# Not useful
|
||||
throwntogethertest_internal-cavity
|
||||
|
|
@ -1276,7 +1302,7 @@ disable_tests(
|
|||
)
|
||||
|
||||
# 2D tests
|
||||
list(APPEND FILES_2D ${FEATURES_2D_FILES} ${SCAD_DXF_FILES} ${EXAMPLE_2D_FILES})
|
||||
list(APPEND FILES_2D ${FEATURES_2D_FILES} ${SCAD_DXF_FILES} ${SCAD_SVG_FILES} ${EXAMPLE_2D_FILES})
|
||||
list(APPEND ALL_2D_FILES ${FILES_2D})
|
||||
|
||||
# FIXME: This test illustrates a weakness in child() combined with modifiers.
|
||||
|
|
@ -1559,6 +1585,8 @@ add_cmdline_test(offcgalpngtest EXE ${PYTHON_EXECUTABLE} SCRIPT ${CMAKE_SOURCE_D
|
|||
|
||||
add_cmdline_test(dxfpngtest EXE ${PYTHON_EXECUTABLE} SCRIPT ${CMAKE_SOURCE_DIR}/export_import_pngtest.py ARGS --openscad=${OPENSCAD_BINPATH} --format=DXF --render=cgal EXPECTEDDIR cgalpngtest SUFFIX png FILES ${FILES_2D})
|
||||
|
||||
add_cmdline_test(svgpngtest EXE ${PYTHON_EXECUTABLE} SCRIPT ${CMAKE_SOURCE_DIR}/export_import_pngtest.py ARGS --openscad=${OPENSCAD_BINPATH} --format=SVG --render=cgal --enable=svg-import EXPECTEDDIR cgalpngtest SUFFIX png FILES ${FILES_2D})
|
||||
|
||||
|
||||
#
|
||||
# Failing tests
|
||||
|
|
|
|||
Loading…
Reference in a new issue