diff --git a/doc/release-notes/0.7.8.md b/doc/release-notes/0.7.8.md new file mode 100644 index 00000000..4ba3bece --- /dev/null +++ b/doc/release-notes/0.7.8.md @@ -0,0 +1,17 @@ +Antimony 0.7.8 +-------------- + +**Features:** +- Human-readable file format + - Friendlier for version control, manual editing. + - Older files will be automatically upgraded. +- `Set colour HSB` node (thanks, [RobotGrrl](https://github.com/mkeeter/antimony/pull/11)) +- Huge speed-up in loading large files (about 20X). +- Minor solver speed-up using `restrict` keyword. + +**Bugfixes:** +- Fixed memory leak in applying `Transform` objects. +- Don't spawn huge number of threads when loading complex files. +- If rendering is interrupted, don't leak images. +- Correct interface between `QGraphicsItem` and `OpenGL`. +- Fix *huge* leak of images and `OpenGL` textures. diff --git a/py/nodes/Color/set_color_hsb.node b/py/nodes/Color/set_color_hsb.node new file mode 100644 index 00000000..1df7b477 --- /dev/null +++ b/py/nodes/Color/set_color_hsb.node @@ -0,0 +1,53 @@ +import fab + +title('Set color (HSB)') + +input("shape", fab.types.Shape) +input("hue", float) +input("saturation", float) +input("brightness", float) + +# copied from Java's HSBtoRGB +# http://www.docjar.com/html/api/java/awt/Color.java.html + +r = 0 +g = 0 +b = 0 + +if saturation == 0: + r = g = b = int(brightness * 255.0 + 0.5) +else: + h = (hue - float(math.floor(hue))) * 6.0 + f = h - float(math.floor(h)) + p = brightness * (1-saturation) + q = brightness * (1-saturation*f) + t = brightness * (1-(saturation*(1-f))) + + h = int(h) + + if(h==0): + r = int(brightness * 255.0 + 0.5) + g = int(t * 255.0 + 0.5) + b = int(p * 255.0 + 0.5) + elif(h==1): + r = int(q * 255.0 + 0.5) + g = int(brightness * 255.0 + 0.5) + b = int(p * 255.0 + 0.5) + elif(h==2): + r = int(p * 255.0 + 0.5) + g = int(brightness * 255.0 + 0.5) + b = int(t * 255.0 + 0.5) + elif(h==3): + r = int(p * 255.0 + 0.5) + g = int(q * 255.0 + 0.5) + b = int(brightness * 255.0 + 0.5) + elif(h==4): + r = int(t * 255.0 + 0.5) + g = int(p * 255.0 + 0.5) + b = int(brightness * 255.0 + 0.5) + elif(h==5): + r = int(brightness * 255.0 + 0.5) + g = int(p * 255.0 + 0.5) + b = int(q * 255.0 + 0.5) + +output("out", fab.shapes.set_color(shape, r, g, b)) diff --git a/qt/fab.pri b/qt/fab.pri index 049eb263..9c617d53 100644 --- a/qt/fab.pri +++ b/qt/fab.pri @@ -15,6 +15,7 @@ SOURCES += \ ../src/fab/formats/png.c \ ../src/fab/formats/stl.c \ ../src/fab/util/region.c \ + ../src/fab/util/ustack.c \ ../src/fab/types/shape.cpp \ ../src/fab/fab.cpp \ ../src/fab/types/bounds.cpp \ @@ -37,6 +38,7 @@ HEADERS += \ ../src/fab/formats/png.h \ ../src/fab/formats/stl.h \ ../src/fab/util/region.h \ + ../src/fab/util/ustack.h \ ../src/fab/types/shape.h \ ../src/fab/fab.h \ ../src/fab/types/bounds.h \ diff --git a/qt/shared.pri b/qt/shared.pri index e88b11ed..fb60b2f4 100644 --- a/qt/shared.pri +++ b/qt/shared.pri @@ -8,6 +8,7 @@ SOURCES += \ ../src/graph/datum/link.cpp \ ../src/graph/node/serializer.cpp \ ../src/graph/node/deserializer.cpp \ + ../src/graph/node/deserializer_old.cpp \ ../src/graph/hooks/hooks.cpp \ ../src/graph/hooks/input.cpp \ ../src/graph/hooks/output.cpp \ @@ -21,6 +22,7 @@ HEADERS += \ ../src/graph/datum/link.h \ ../src/graph/node/serializer.h \ ../src/graph/node/deserializer.h \ + ../src/graph/node/deserializer_old.h \ ../src/graph/hooks/hooks.h \ ../src/graph/hooks/input.h \ ../src/graph/hooks/output.h \ diff --git a/src/app/app.cpp b/src/app/app.cpp index 8cf459e8..72cb0444 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -34,6 +34,7 @@ #include "graph/datum/link.h" #include "graph/node/serializer.h" #include "graph/node/deserializer.h" +#include "graph/node/deserializer_old.h" #include "fab/types/shape.h" @@ -143,8 +144,7 @@ void App::onSave() SceneSerializer ss(root, graph_scene->inspectorPositions()); - QDataStream out(&file); - ss.run(&out); + file.write(QJsonDocument(ss.run()).toJson()); stack->setClean(); } @@ -200,16 +200,30 @@ void App::loadFile(QString f) return; } - QDataStream in(&file); SceneDeserializer ds(root); - ds.run(&in); + ds.run(QJsonDocument::fromJson(file.readAll()).object()); if (ds.failed == true) { - QMessageBox::critical(NULL, "Loading error", - "Loading error:
" + - ds.error_message); - onNew(); + // Attempt to load the file with the old deserializer + file.reset(); + QDataStream in(&file); + SceneDeserializerOld dso(root); + dso.run(&in); + + if (dso.failed) + { + QMessageBox::critical(NULL, "Loading error", + "Loading error:
" + + dso.error_message); + onNew(); + } + else + { + makeUI(root); + graph_scene->setInspectorPositions(dso.inspectors); + emit(windowTitleChanged(getWindowTitle())); + } } else { // If there's a warning message, show it in a box. if (!ds.warning_message.isNull()) @@ -639,6 +653,7 @@ void App::newNode(Node* n) void App::makeUI(NodeRoot* r) { + QList nodes; QList datums; QMap> links; @@ -659,7 +674,8 @@ void App::makeUI(NodeRoot* r) k->setParent(NULL); } } - newNode(n); + graph_scene->makeUIfor(n); + nodes.append(n); } for (auto i = links.begin(); i != links.end(); ++i) @@ -675,7 +691,15 @@ void App::makeUI(NodeRoot* r) // Now that all links are created and all nodes are under the same // root (in case of address-by-name), run update on every datum. for (auto d : datums) - d->update(); + if (!d->getValid()) + d->update(); + + // Finally, make render workers for all of the new nodes. + // This needs to happen after connections are made; otherwise + // we end up with a ton of RenderWorkers that all start rendering + // at once (and eat up all of the threads). + for (auto n : nodes) + view_scene->makeRenderWorkersFor(n); } Connection* App::newLink(Link* link) diff --git a/src/app/undo/undo_delete_node.h b/src/app/undo/undo_delete_node.h index 0f06e70a..2c79d51d 100644 --- a/src/app/undo/undo_delete_node.h +++ b/src/app/undo/undo_delete_node.h @@ -3,6 +3,7 @@ #include #include +#include #include "app/undo/undo_command.h" @@ -28,7 +29,7 @@ class UndoDeleteNodeCommand : public UndoCommand QList nodes; QMap datums; - QByteArray data; + QJsonObject data; }; #endif diff --git a/src/fab/tree/math/math_r.c b/src/fab/tree/math/math_r.c index 34d22f07..c899281f 100644 --- a/src/fab/tree/math/math_r.c +++ b/src/fab/tree/math/math_r.c @@ -3,49 +3,56 @@ #include "tree/math/math_r.h" -float* add_r(float* A, float* B, float* R, int c) +float* add_r(const float* restrict A, const float* restrict B, + float* restrict R, int c) { for (int q = 0; q < c; ++q) R[q] = A[q] + B[q]; return R; } -float* sub_r(float* A, float* B, float* R, int c) +float* sub_r(const float* restrict A, const float* restrict B, + float* restrict R, int c) { for (int q = 0; q < c; ++q) R[q] = A[q] - B[q]; return R; } -float* mul_r(float* A, float* B, float* R, int c) +float* mul_r(const float* restrict A, const float* restrict B, + float* R, int c) { for (int q = 0; q < c; ++q) R[q] = A[q] * B[q]; return R; } -float* div_r(float* A, float* B, float* R, int c) +float* div_r(const float* restrict A, const float* restrict B, + float* R, int c) { for (int q = 0; q < c; ++q) R[q] = A[q] / B[q]; return R; } -float* min_r(float* A, float* B, float* R, int c) +float* min_r(const float* restrict A, const float* restrict B, + float* R, int c) { for (int q = 0; q < c; ++q) R[q] = fmin(A[q], B[q]); return R; } -float* max_r(float* A, float* B, float* R, int c) +float* max_r(const float* restrict A, const float* restrict B, + float* R, int c) { for (int q = 0; q < c; ++q) R[q] = fmax(A[q], B[q]); return R; } -float* pow_r(float* A, float* B, float* R, int c) +float* pow_r(const float* restrict A, const float* restrict B, + float* R, int c) { for (int q = 0; q < c; ++q) R[q] = pow(A[q], B[q]); @@ -54,21 +61,21 @@ float* pow_r(float* A, float* B, float* R, int c) //////////////////////////////////////////////////////////////////////////////// -float* abs_r(float* A, float* R, int c) +float* abs_r(const float* restrict A, float* R, int c) { for (int q=0; q < c; ++q) R[q] = fabs(A[q]); return R; } -float* square_r(float* A, float* R, int c) +float* square_r(const float* restrict A, float* R, int c) { for (int q = 0; q < c; ++q) R[q] = A[q]*A[q]; return R; } -float* sqrt_r(float* A, float* R, int c) +float* sqrt_r(const float* restrict A, float* R, int c) { for (int q = 0; q < c; ++q) if (A[q] < 0) R[q] = 0; @@ -76,35 +83,35 @@ float* sqrt_r(float* A, float* R, int c) return R; } -float* sin_r(float* A, float* R, int c) +float* sin_r(const float* restrict A, float* R, int c) { for (int q = 0; q < c; ++q) R[q] = sin(A[q]); return R; } -float* cos_r(float* A, float* R, int c) +float* cos_r(const float* restrict A, float* R, int c) { for (int q = 0; q < c; ++q) R[q] = cos(A[q]); return R; } -float* tan_r(float* A, float* R, int c) +float* tan_r(const float* restrict A, float* R, int c) { for (int q = 0; q < c; ++q) R[q] = tan(A[q]); return R; } -float* asin_r(float* A, float* R, int c) +float* asin_r(const float* restrict A, float* R, int c) { for (int q = 0; q < c; ++q) R[q] = asin(A[q]); return R; } -float* acos_r(float* A, float* R, int c) +float* acos_r(const float* restrict A, float* R, int c) { for (int q = 0; q < c; ++q) { if (A[q] < -1) R[q] = M_PI; @@ -114,21 +121,21 @@ float* acos_r(float* A, float* R, int c) return R; } -float* atan_r(float* A, float* R, int c) +float* atan_r(const float* restrict A, float* R, int c) { for (int q = 0; q < c; ++q) R[q] = atan(A[q]); return R; } -float* neg_r(float* A, float* R, int c) +float* neg_r(const float* restrict A, float* R, int c) { for (int q = 0; q < c; ++q) R[q] = -A[q]; return R; } -float* exp_r(float* A, float* R, int c) +float* exp_r(const float* restrict A, float* R, int c) { for (int q = 0; q < c; ++q) R[q] = exp(A[q]); @@ -137,19 +144,19 @@ float* exp_r(float* A, float* R, int c) //////////////////////////////////////////////////////////////////////////////// -float* X_r(float* X, float* R, int c) +float* X_r(const float* restrict X, float* R, int c) { memcpy(R, X,c*sizeof(float)); return R; } -float* Y_r(float* Y, float* R, int c) +float* Y_r(const float* restrict Y, float* R, int c) { memcpy(R, Y, c*sizeof(float)); return R; } -float* Z_r(float* Z, float* R, int c) +float* Z_r(const float* restrict Z, float* R, int c) { memcpy(R, Z, c*sizeof(float)); return R; diff --git a/src/fab/tree/math/math_r.h b/src/fab/tree/math/math_r.h index d4f0fb8c..366324fa 100644 --- a/src/fab/tree/math/math_r.h +++ b/src/fab/tree/math/math_r.h @@ -12,33 +12,33 @@ extern "C" { */ // Binary functions -float* add_r(float* A, float* B, float* R, int c); -float* sub_r(float* A, float* B, float* R, int c); -float* mul_r(float* A, float* B, float* R, int c); -float* div_r(float* A, float* B, float* R, int c); +float* add_r(const float* A, const float* B, float* R, int c); +float* sub_r(const float* A, const float* B, float* R, int c); +float* mul_r(const float* A, const float* B, float* R, int c); +float* div_r(const float* A, const float* B, float* R, int c); -float* min_r(float* A, float* B, float* R, int c); -float* max_r(float* A, float* B, float* R, int c); +float* min_r(const float* A, const float* B, float* R, int c); +float* max_r(const float* A, const float* B, float* R, int c); -float* pow_r(float* A, float* B, float* R, int c); +float* pow_r(const float* A, const float* B, float* R, int c); // Unary functions -float* abs_r(float* A, float* R, int c); -float* square_r(float* A, float* R, int c); -float* sqrt_r(float* A, float* R, int c); -float* sin_r(float* A, float* R, int c); -float* cos_r(float* A, float* R, int c); -float* tan_r(float* A, float* R, int c); -float* asin_r(float* A, float* R, int c); -float* acos_r(float* A, float* R, int c); -float* atan_r(float* A, float* R, int c); -float* neg_r(float* A, float* R, int c); -float* exp_r(float* A, float* R, int c); +float* abs_r(const float* A, float* R, int c); +float* square_r(const float* A, float* R, int c); +float* sqrt_r(const float* A, float* R, int c); +float* sin_r(const float* A, float* R, int c); +float* cos_r(const float* A, float* R, int c); +float* tan_r(const float* A, float* R, int c); +float* asin_r(const float* A, float* R, int c); +float* acos_r(const float* A, float* R, int c); +float* atan_r(const float* A, float* R, int c); +float* neg_r(const float* A, float* R, int c); +float* exp_r(const float* A, float* R, int c); // Variables -float* X_r(float* X, float* R, int c); -float* Y_r(float* Y, float* R, int c); -float* Z_r(float* Z, float* R, int c); +float* X_r(const float* X, float* R, int c); +float* Y_r(const float* Y, float* R, int c); +float* Z_r(const float* Z, float* R, int c); #ifdef __cplusplus } diff --git a/src/fab/tree/tree.c b/src/fab/tree/tree.c index ad9d486b..0040a71d 100644 --- a/src/fab/tree/tree.c +++ b/src/fab/tree/tree.c @@ -16,7 +16,7 @@ MathTree* new_tree(unsigned num_levels, unsigned num_constants) .active = num_levels ? calloc(num_levels, sizeof(unsigned)) : NULL, .disabled = num_levels ? - calloc(num_levels, sizeof(ustack*)) : NULL, + calloc(num_levels, sizeof(ustack)) : NULL, .constants = num_constants ? malloc(sizeof(Node*)*num_constants) : NULL, @@ -38,6 +38,7 @@ void free_tree(MathTree* tree) free(tree->nodes[level][n]); } free(tree->nodes[level]); + free(tree->disabled[level].data); } for (unsigned c=0; c < tree->num_constants; ++c) { @@ -47,6 +48,7 @@ void free_tree(MathTree* tree) free(tree->nodes); free(tree->active); free(tree->constants); + free(tree->disabled); free(tree); } @@ -126,7 +128,7 @@ void disable_node(MathTree* tree, int level, int n) tree->nodes[level][back] = node; // Finally, increase the count of disabled nodes - tree->disabled[level]->count++; + ustack_increment(&tree->disabled[level]); } @@ -181,9 +183,7 @@ void disable_nodes(MathTree* tree) // Save the number of nodes disabled in this pass so that // we can reverse the operation later. - ustack* tmp = tree->disabled[level]; - tree->disabled[level] = malloc(sizeof(ustack)); - *(tree->disabled[level]) = (ustack){0, tmp}; + ustack_push(&tree->disabled[level], 0); for (int n=0; n < tree->active[level]; ++n) { Node* node = tree->nodes[level][n]; @@ -235,10 +235,7 @@ void disable_nodes(MathTree* tree) void enable_nodes(MathTree* tree) { for (int level=tree->num_levels-1; level >= 0; --level) { - tree->active[level] += tree->disabled[level]->count; - ustack* next = tree->disabled[level]->next; - free(tree->disabled[level]); - tree->disabled[level] = next; + tree->active[level] += ustack_pop(&tree->disabled[level]); } } diff --git a/src/fab/tree/tree.h b/src/fab/tree/tree.h index 02e40ac9..44f8bd00 100644 --- a/src/fab/tree/tree.h +++ b/src/fab/tree/tree.h @@ -4,24 +4,12 @@ #include #include "tree/node/opcodes.h" +#include "util/ustack.h" #ifdef __cplusplus extern "C" { #endif -/** @struct ustack_ - @brief A simple FIFO stack of unsigned integers. -*/ -typedef struct ustack_ { - /** @var count - Stored value */ - unsigned count; - - /** @var next - Next item in the stack */ - struct ustack_* next; -} ustack; - /** @struct MathTree_ @brief A structure containing nodes organized by rank and opcode. */ @@ -36,7 +24,7 @@ typedef struct MathTree_ { /** @var disabled Stacks of disabled node counts, indexed by level */ - ustack** disabled; + ustack* disabled; /** @var num_levels Number of levels in this tree */ diff --git a/src/fab/types/bounds.cpp b/src/fab/types/bounds.cpp index d42f2bf7..fccf2a3b 100644 --- a/src/fab/types/bounds.cpp +++ b/src/fab/types/bounds.cpp @@ -48,7 +48,7 @@ Bounds Bounds::map(Transform t) const throw fab::ParseError(); } x_out = eval_i(tree, x, y, z); - free(tree); + free_tree(tree); } if (t.y_reverse.length()) @@ -59,7 +59,7 @@ Bounds Bounds::map(Transform t) const throw fab::ParseError(); } y_out = eval_i(tree, x, y, z); - free(tree); + free_tree(tree); } if (t.z_reverse.length()) @@ -70,7 +70,7 @@ Bounds Bounds::map(Transform t) const throw fab::ParseError(); } z_out = eval_i(tree, x, y, z); - free(tree); + free_tree(tree); } return Bounds(x_out.lower, y_out.lower, z_out.lower, diff --git a/src/fab/util/ustack.c b/src/fab/util/ustack.c new file mode 100644 index 00000000..76c4bdeb --- /dev/null +++ b/src/fab/util/ustack.c @@ -0,0 +1,33 @@ +#include "fab/util/ustack.h" + +void ustack_push(ustack* u, unsigned i) +{ + if (u->allocated == 0) + { + u->allocated = 2; + u->data = malloc(u->allocated * sizeof(unsigned)); + } + else if (u->ptr >= u->allocated) + { + u->allocated *= 2; + u->data = realloc(u->data, u->allocated * sizeof(unsigned)); + } + + u->data[u->ptr++] = i; +} + +unsigned ustack_pop(ustack* u) +{ + return u->data[--u->ptr]; +} + +void ustack_free(ustack* u) +{ + free(u->data); + free(u); +} + +void ustack_increment(ustack* u) +{ + u->data[u->ptr - 1]++; +} diff --git a/src/fab/util/ustack.h b/src/fab/util/ustack.h new file mode 100644 index 00000000..abd4590b --- /dev/null +++ b/src/fab/util/ustack.h @@ -0,0 +1,36 @@ +#ifndef USTACK_H +#define USTACK_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* A stack of unsigned integers. + */ +typedef struct ustack_ { + unsigned* data; + + size_t ptr; + size_t allocated; +} ustack; + +// Pushes a new value to the stack +void ustack_push(ustack* u, unsigned i); + +// Pops the top value from the stack +unsigned ustack_pop(ustack* u); + +// Frees a ustack struct +void ustack_free(ustack* u); + +// Increments the top value on the stack +void ustack_increment(ustack* u); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/graph/datum/datum.cpp b/src/graph/datum/datum.cpp index 8415b4ea..723a159d 100644 --- a/src/graph/datum/datum.cpp +++ b/src/graph/datum/datum.cpp @@ -10,6 +10,15 @@ #include "graph/node/node.h" #include "graph/node/root.h" +#include "graph/datum/datums/float_datum.h" +#include "graph/datum/datums/float_output_datum.h" +#include "graph/datum/datums/int_datum.h" +#include "graph/datum/datums/name_datum.h" +#include "graph/datum/datums/string_datum.h" +#include "graph/datum/datums/script_datum.h" +#include "graph/datum/datums/shape_output_datum.h" +#include "graph/datum/datums/shape_datum.h" + Datum::Datum(QString name, Node* parent) : QObject(parent), value(NULL), valid(false), input_handler(NULL), post_init_called(false) @@ -25,6 +34,29 @@ Datum::~Datum() Py_XDECREF(value); } +Datum* Datum::fromTypeString(QString type, QString name, Node* parent) +{ + if (type == FloatDatum::typeString()) + return new FloatDatum(name, parent); + else if (type == FloatOutputDatum::typeString()) + return new FloatOutputDatum(name, parent); + else if (type == IntDatum::typeString()) + return new IntDatum(name, parent); + else if (type == NameDatum::typeString()) + return new NameDatum(name, parent); + else if (type == StringDatum::typeString()) + return new StringDatum(name, parent); + else if (type == ScriptDatum::typeString()) + return new ScriptDatum(name, parent); + else if (type == ShapeOutputDatum::typeString()) + return new ShapeOutputDatum(name, parent); + else if (type == ShapeDatum::typeString()) + return new ShapeDatum(name, parent); + + Q_ASSERT(false); + return NULL; +} + bool Datum::hasInputValue() const { return input_handler != NULL && input_handler->hasInput(); diff --git a/src/graph/datum/datum.h b/src/graph/datum/datum.h index 989a856d..940f8af6 100644 --- a/src/graph/datum/datum.h +++ b/src/graph/datum/datum.h @@ -29,6 +29,9 @@ class Datum : public QObject * (used in serialiation). */ virtual DatumType::DatumType getDatumType() const=0; + virtual QString getDatumTypeString() const=0; + + static Datum* fromTypeString(QString type, QString name, Node* parent); /** Returns desired Python type * diff --git a/src/graph/datum/datums/float_datum.h b/src/graph/datum/datums/float_datum.h index 7ccb6afe..67d7ccc5 100644 --- a/src/graph/datum/datums/float_datum.h +++ b/src/graph/datum/datums/float_datum.h @@ -14,6 +14,9 @@ class FloatDatum : public EvalDatum DatumType::DatumType getDatumType() const override { return DatumType::FLOAT; } + static QString typeString() { return "float"; } + QString getDatumTypeString() const override { return typeString(); } + /* * Attempts to add delta to the current value. * diff --git a/src/graph/datum/datums/float_output_datum.h b/src/graph/datum/datums/float_output_datum.h index b2ea7534..4e9f1553 100644 --- a/src/graph/datum/datums/float_output_datum.h +++ b/src/graph/datum/datums/float_output_datum.h @@ -12,6 +12,9 @@ class FloatOutputDatum : public OutputDatum DatumType::DatumType getDatumType() const override { return DatumType::FLOAT_OUTPUT; } + static QString typeString() { return "float output"; } + QString getDatumTypeString() const override { return typeString(); } + QString getString() const override; }; diff --git a/src/graph/datum/datums/int_datum.h b/src/graph/datum/datums/int_datum.h index 9c8b766d..508e501f 100644 --- a/src/graph/datum/datums/int_datum.h +++ b/src/graph/datum/datums/int_datum.h @@ -14,6 +14,9 @@ class IntDatum : public EvalDatum DatumType::DatumType getDatumType() const override { return DatumType::INT; } + static QString typeString() { return "int"; } + QString getDatumTypeString() const override { return typeString(); } + /* * Attempts to add delta to the current value. * diff --git a/src/graph/datum/datums/name_datum.h b/src/graph/datum/datums/name_datum.h index 29b99a26..8c048cc0 100644 --- a/src/graph/datum/datums/name_datum.h +++ b/src/graph/datum/datums/name_datum.h @@ -15,6 +15,9 @@ class NameDatum : public EvalDatum DatumType::DatumType getDatumType() const override { return DatumType::NAME; } + static QString typeString() { return "name"; } + QString getDatumTypeString() const override { return typeString(); } + signals: void nameChanged(QString new_name); public slots: diff --git a/src/graph/datum/datums/script_datum.h b/src/graph/datum/datums/script_datum.h index 0b75dd38..e75304e1 100644 --- a/src/graph/datum/datums/script_datum.h +++ b/src/graph/datum/datums/script_datum.h @@ -29,6 +29,9 @@ class ScriptDatum : public EvalDatum DatumType::DatumType getDatumType() const override { return DatumType::SCRIPT; } + static QString typeString() { return "script"; } + QString getDatumTypeString() const override { return typeString(); } + QString getOutput() const { return output; } protected: diff --git a/src/graph/datum/datums/shape_datum.h b/src/graph/datum/datums/shape_datum.h index c429ea23..18a38dcc 100644 --- a/src/graph/datum/datums/shape_datum.h +++ b/src/graph/datum/datums/shape_datum.h @@ -16,6 +16,9 @@ class ShapeDatum : public EvalDatum DatumType::DatumType getDatumType() const override { return DatumType::SHAPE; } + static QString typeString() { return "shape"; } + QString getDatumTypeString() const override { return typeString(); } + // Always return false for hasOutput, otherwise that output port // will be seen as something that should be rendered. bool hasOutput() const { return false; } diff --git a/src/graph/datum/datums/shape_output_datum.h b/src/graph/datum/datums/shape_output_datum.h index a5e8cd50..c30928ad 100644 --- a/src/graph/datum/datums/shape_output_datum.h +++ b/src/graph/datum/datums/shape_output_datum.h @@ -11,6 +11,9 @@ class ShapeOutputDatum : public OutputDatum PyTypeObject* getType() const override; DatumType::DatumType getDatumType() const override { return DatumType::SHAPE_OUTPUT; } + + static QString typeString() { return "shape output"; } + QString getDatumTypeString() const override { return typeString(); } }; #endif diff --git a/src/graph/datum/datums/string_datum.h b/src/graph/datum/datums/string_datum.h index dcf26415..3c6b63ea 100644 --- a/src/graph/datum/datums/string_datum.h +++ b/src/graph/datum/datums/string_datum.h @@ -13,6 +13,9 @@ class StringDatum : public EvalDatum DatumType::DatumType getDatumType() const override { return DatumType::STRING; } + static QString typeString() { return "string"; } + QString getDatumTypeString() const override { return typeString(); } + protected: QString prepareExpr(QString s) const override; PyObject* getCurrentValue() override; diff --git a/src/graph/node/deserializer.cpp b/src/graph/node/deserializer.cpp index e630cf13..4b1a4477 100644 --- a/src/graph/node/deserializer.cpp +++ b/src/graph/node/deserializer.cpp @@ -11,14 +11,7 @@ #include "graph/node/node.h" #include "graph/node/root.h" -#include "graph/datum/datums/float_datum.h" -#include "graph/datum/datums/float_output_datum.h" -#include "graph/datum/datums/int_datum.h" -#include "graph/datum/datums/name_datum.h" -#include "graph/datum/datums/string_datum.h" -#include "graph/datum/datums/script_datum.h" -#include "graph/datum/datums/shape_output_datum.h" -#include "graph/datum/datums/shape_datum.h" +#include "graph/datum/types/eval_datum.h" SceneDeserializer::SceneDeserializer(NodeRoot* node_root) : QObject(), failed(false), node_root(node_root) @@ -26,143 +19,109 @@ SceneDeserializer::SceneDeserializer(NodeRoot* node_root) // Nothing to do here } -bool SceneDeserializer::run(QByteArray in) +bool SceneDeserializer::run(QJsonObject in) { - QBuffer buffer(&in); - buffer.open(QBuffer::ReadOnly); - - QDataStream stream(&buffer); - return run(&stream); -} - -bool SceneDeserializer::run(QDataStream* in) -{ - QString sb; - *in >> sb >> protocol_version; - - if (sb != "sb") + // Check that the "type" field is "sb" + if (in.find("type") == in.end() || in["type"].toString() != "sb") { failed = true; error_message = "File is not an Antimony file"; } - else if (protocol_version < 2) + + // Check file saving protocol + if (in.find("protocol") == in.end()) { failed = true; - error_message = "File was saved with an older protocol and can no longer be read."; + error_message = "Could not detect protocol"; } - else if (protocol_version == 2) + else { - failed = true; - error_message = - "File was saved with an older protocol and cannot be read.
" - "Open it in Antimony 0.7.6c and re-save to upgrade file protocol."; + protocol_version = in["protocol"].toDouble(); + if (protocol_version < 5) + { + failed = true; + error_message = "File was saved with a older protocol and cannot yet be read."; + } + else if (protocol_version > 5) + { + failed = true; + error_message = "File was saved with a newer protocol and cannot yet be read."; + } } - else if (protocol_version > 4) + + // Make sure there's a "nodes" array + if (in.find("nodes") == in.end() || !in["nodes"].isArray()) { failed = true; - error_message = "File was saved with a newer protocol and cannot yet be read."; + error_message = "File does not contain any nodes."; } if (!failed) { - deserializeNodes(in, node_root); - deserializeConnections(in); + deserializeNodes(in["nodes"].toArray(), node_root); + if (in.find("connections") != in.end()) + deserializeConnections(in["connections"].toArray()); } return failed; } -void SceneDeserializer::deserializeNodes(QDataStream* in, NodeRoot* p) +void SceneDeserializer::deserializeNodes(QJsonArray in, NodeRoot* p) { - quint32 count; - *in >> count; - for (unsigned i=0; i < count; ++i) - deserializeNode(in, p); + for (auto n : in) + deserializeNode(n.toObject(), p); } -void SceneDeserializer::deserializeNode(QDataStream* in, NodeRoot* p) +void SceneDeserializer::deserializeNode(QJsonObject in, NodeRoot* p) { - quint32 t; - *in >> t; // Deserialize dummy node type - QString node_name; - *in >> node_name; - Node* node = new Node(p); - node->setObjectName(node_name); // Deserialize inspector position - QPointF i; - *in >> i; - inspectors[node] = i; - - quint32 datum_count; - *in >> datum_count; - for (unsigned d=0; d < datum_count; ++d) - deserializeDatum(in, node); -} + auto a = in["inspector"].toArray(); + inspectors[node] = QPointF(a[0].toDouble(), a[1].toDouble()); -void SceneDeserializer::deserializeDatum(QDataStream* in, Node* node) -{ - quint32 t; - *in >> t; - QString name; - *in >> name; - - if (protocol_version == 3 && name == "_name") - name = "__name"; - if (protocol_version == 3 && name == "_script") - name = "__script"; + for (auto d : in["datums"].toArray()) + deserializeDatum(d.toObject(), node); - DatumType::DatumType datum_type = static_cast(t); + nodes << node; +} - Datum* datum; +void SceneDeserializer::deserializeDatum(QJsonObject in, Node* node) +{ + QString type = in["type"].toString(); + QString name = in["name"].toString(); - switch (datum_type) - { - case DatumType::FLOAT: - datum = new FloatDatum(name, node); break; - case DatumType::FLOAT_OUTPUT: - datum = new FloatOutputDatum(name, node); break; - case DatumType::INT: - datum = new IntDatum(name, node); break; - case DatumType::NAME: - datum = new NameDatum(name, node); break; - case DatumType::STRING: - datum = new StringDatum(name, node); break; - case DatumType::SCRIPT: - datum = new ScriptDatum(name, node); break; - case DatumType::SHAPE_OUTPUT: - datum = new ShapeOutputDatum(name, node); break; - case DatumType::SHAPE_INPUT: // Automatically upgrade SHAPE_INPUT to SHAPE - case DatumType::SHAPE: - datum = new ShapeDatum(name, node); break; - case DatumType::SHAPE_FUNCTION: - datum = NULL; - Q_ASSERT(false); // this is a deprecated Datum type. - } + Datum* datum = Datum::fromTypeString(type, name, node); - auto e = dynamic_cast(datum); - // Special case when upgrading SHAPE_INPUT datums to SHAPE datums: - // They weren't serialized with an expression, so don't try to read it. - if (e && !(protocol_version == 3 && datum_type == DatumType::SHAPE_INPUT)) + if (auto e = dynamic_cast(datum)) { - QString expr; - *in >> expr; - e->setExpr(expr); + if (in["expr"].isArray()) + { + QStringList a; + for (auto line : in["expr"].toArray()) + a.append(line.toString()); + e->setExpr(a.join("\n")); + } + else + { + e->setExpr(in["expr"].toString()); + } } - - datums << datum; } -void SceneDeserializer::deserializeConnections(QDataStream* in) +void SceneDeserializer::deserializeConnections(QJsonArray in) { - quint32 count; - *in >> count; - - for (unsigned i=0; i < count; ++i) + for (auto c_ : in) { - quint32 source_index, target_index; - *in >> source_index >> target_index; - datums[target_index]->addLink(datums[source_index]->linkFrom()); + auto c = c_.toArray(); + auto start = c[0].toArray(); + auto end = c[1].toArray(); + + auto start_datum = nodes[start[0].toDouble()]->findChild( + start[1].toString(), Qt::FindDirectChildrenOnly); + auto end_datum = nodes[end[0].toDouble()]->findChild( + end[1].toString(), Qt::FindDirectChildrenOnly); + + end_datum->addLink(start_datum->linkFrom()); } } diff --git a/src/graph/node/deserializer.h b/src/graph/node/deserializer.h index f8820ed5..c8aa46ea 100644 --- a/src/graph/node/deserializer.h +++ b/src/graph/node/deserializer.h @@ -4,8 +4,9 @@ #include #include #include +#include +#include -class Datum; class Node; class NodeRoot; @@ -15,8 +16,7 @@ class SceneDeserializer : public QObject public: explicit SceneDeserializer(NodeRoot* node_root); - bool run(QDataStream* in); - bool run(QByteArray in); + bool run(QJsonObject in); QMap inspectors; bool failed; @@ -24,14 +24,14 @@ class SceneDeserializer : public QObject QString warning_message; protected: - void deserializeNodes(QDataStream* in, NodeRoot* p); - void deserializeNode(QDataStream* in, NodeRoot* p); - void deserializeDatum(QDataStream* in, Node* node); - void deserializeConnections(QDataStream* in); + void deserializeNodes(QJsonArray in, NodeRoot* p); + void deserializeNode(QJsonObject in, NodeRoot* p); + void deserializeDatum(QJsonObject in, Node* node); + void deserializeConnections(QJsonArray in); quint32 protocol_version; NodeRoot* node_root; - QList datums; + QList nodes; }; #endif // DESERIALIZER_H diff --git a/src/graph/node/deserializer_old.cpp b/src/graph/node/deserializer_old.cpp new file mode 100644 index 00000000..36dc6d32 --- /dev/null +++ b/src/graph/node/deserializer_old.cpp @@ -0,0 +1,172 @@ +#include + +#include +#include +#include +#include + +#include "app/app.h" + +#include "graph/node/deserializer_old.h" +#include "graph/node/node.h" +#include "graph/node/root.h" + +#include "graph/datum/datums/float_datum.h" +#include "graph/datum/datums/float_output_datum.h" +#include "graph/datum/datums/int_datum.h" +#include "graph/datum/datums/name_datum.h" +#include "graph/datum/datums/string_datum.h" +#include "graph/datum/datums/script_datum.h" +#include "graph/datum/datums/shape_output_datum.h" +#include "graph/datum/datums/shape_datum.h" + +SceneDeserializerOld::SceneDeserializerOld(NodeRoot* node_root) + : QObject(), failed(false), node_root(node_root) +{ + // Nothing to do here +} + +bool SceneDeserializerOld::run(QByteArray in) +{ + QBuffer buffer(&in); + buffer.open(QBuffer::ReadOnly); + + QDataStream stream(&buffer); + return run(&stream); +} + +bool SceneDeserializerOld::run(QDataStream* in) +{ + QString sb; + *in >> sb >> protocol_version; + + if (sb != "sb") + { + failed = true; + error_message = "File is not an Antimony file"; + } + else if (protocol_version < 2) + { + failed = true; + error_message = "File was saved with an older protocol and can no longer be read."; + } + else if (protocol_version == 2) + { + failed = true; + error_message = + "File was saved with an older protocol and cannot be read.
" + "Open it in Antimony 0.7.6c and re-save to upgrade file protocol."; + } + else if (protocol_version == 3) + { + failed = true; + error_message = + "File was saved with an older protocol and cannot be read.
" + "Open it in Antimony 0.7.7 and re-save to upgrade file protocol."; + } + else if (protocol_version > 4) + { + failed = true; + error_message = "File was saved with a newer protocol and cannot yet be read."; + } + + if (!failed) + { + deserializeNodes(in, node_root); + deserializeConnections(in); + } + + return failed; +} + +void SceneDeserializerOld::deserializeNodes(QDataStream* in, NodeRoot* p) +{ + quint32 count; + *in >> count; + for (unsigned i=0; i < count; ++i) + deserializeNode(in, p); +} + +void SceneDeserializerOld::deserializeNode(QDataStream* in, NodeRoot* p) +{ + quint32 t; + *in >> t; // Deserialize dummy node type + QString node_name; + *in >> node_name; + + Node* node = new Node(p); + node->setObjectName(node_name); + + // Deserialize inspector position + QPointF i; + *in >> i; + inspectors[node] = i; + + quint32 datum_count; + *in >> datum_count; + for (unsigned d=0; d < datum_count; ++d) + deserializeDatum(in, node); +} + +void SceneDeserializerOld::deserializeDatum(QDataStream* in, Node* node) +{ + quint32 t; + *in >> t; + QString name; + *in >> name; + + if (protocol_version == 3 && name == "_name") + name = "__name"; + if (protocol_version == 3 && name == "_script") + name = "__script"; + + DatumType::DatumType datum_type = static_cast(t); + + Datum* datum; + + switch (datum_type) + { + case DatumType::FLOAT: + datum = new FloatDatum(name, node); break; + case DatumType::FLOAT_OUTPUT: + datum = new FloatOutputDatum(name, node); break; + case DatumType::INT: + datum = new IntDatum(name, node); break; + case DatumType::NAME: + datum = new NameDatum(name, node); break; + case DatumType::STRING: + datum = new StringDatum(name, node); break; + case DatumType::SCRIPT: + datum = new ScriptDatum(name, node); break; + case DatumType::SHAPE_OUTPUT: + datum = new ShapeOutputDatum(name, node); break; + case DatumType::SHAPE: + datum = new ShapeDatum(name, node); break; + case DatumType::SHAPE_INPUT: + case DatumType::SHAPE_FUNCTION: + datum = NULL; + Q_ASSERT(false); // this is a deprecated Datum type. + } + + if (auto e = dynamic_cast(datum)) + { + QString expr; + *in >> expr; + e->setExpr(expr); + } + + datums << datum; +} + +void SceneDeserializerOld::deserializeConnections(QDataStream* in) +{ + quint32 count; + *in >> count; + + for (unsigned i=0; i < count; ++i) + { + quint32 source_index, target_index; + *in >> source_index >> target_index; + datums[target_index]->addLink(datums[source_index]->linkFrom()); + } +} diff --git a/src/graph/node/deserializer_old.h b/src/graph/node/deserializer_old.h new file mode 100644 index 00000000..91223891 --- /dev/null +++ b/src/graph/node/deserializer_old.h @@ -0,0 +1,37 @@ +#ifndef DESERIALIZER_OLD_H +#define DESERIALIZER_OLD_H + +#include +#include +#include + +class Datum; +class Node; +class NodeRoot; + +class SceneDeserializerOld : public QObject +{ + Q_OBJECT +public: + explicit SceneDeserializerOld(NodeRoot* node_root); + + bool run(QDataStream* in); + bool run(QByteArray in); + + QMap inspectors; + bool failed; + QString error_message; + QString warning_message; + +protected: + void deserializeNodes(QDataStream* in, NodeRoot* p); + void deserializeNode(QDataStream* in, NodeRoot* p); + void deserializeDatum(QDataStream* in, Node* node); + void deserializeConnections(QDataStream* in); + + quint32 protocol_version; + NodeRoot* node_root; + QList datums; +}; + +#endif // DESERIALIZER_OLD_H diff --git a/src/graph/node/node.cpp b/src/graph/node/node.cpp index d86b091e..06b33f3b 100644 --- a/src/graph/node/node.cpp +++ b/src/graph/node/node.cpp @@ -52,8 +52,7 @@ void Node::updateName() PyObject* Node::proxy(Datum* caller, bool settable) { - auto _proxy_module = PyImport_ImportModule("_proxy"); - auto p = PyObject_CallMethod(_proxy_module, "NodeProxy", NULL); + auto p = PyObject_CallObject(proxy::proxyConstructor(), NULL); Q_ASSERT(!PyErr_Occurred()); auto& proxy = boost::python::extract(p)(); @@ -61,7 +60,6 @@ PyObject* Node::proxy(Datum* caller, bool settable) proxy.caller = caller; proxy.settable = settable; - Py_DECREF(_proxy_module); return p; } diff --git a/src/graph/node/proxy.cpp b/src/graph/node/proxy.cpp index 691b4b2d..e2a15219 100644 --- a/src/graph/node/proxy.cpp +++ b/src/graph/node/proxy.cpp @@ -100,3 +100,16 @@ void proxy::preInit() { PyImport_AppendInittab("_proxy", PyInit__proxy); } + +PyObject* proxy::proxy_init = NULL; + +PyObject* proxy::proxyConstructor() +{ + if (proxy_init == NULL) + { + auto _proxy_module = PyImport_ImportModule("_proxy"); + proxy_init = PyObject_GetAttrString(_proxy_module, "NodeProxy"); + Py_DECREF(_proxy_module); + } + return proxy_init; +} diff --git a/src/graph/node/proxy.h b/src/graph/node/proxy.h index da667214..34f4ba02 100644 --- a/src/graph/node/proxy.h +++ b/src/graph/node/proxy.h @@ -24,6 +24,9 @@ namespace proxy { }; void onProxyException(const ProxyException& e); void preInit(); + PyObject* proxyConstructor(); + + extern PyObject* proxy_init; }; #endif // NODE_PROXY_H diff --git a/src/graph/node/serializer.cpp b/src/graph/node/serializer.cpp index 701f301f..ddd3d612 100644 --- a/src/graph/node/serializer.cpp +++ b/src/graph/node/serializer.cpp @@ -1,10 +1,10 @@ #include -#include -#include +#include #include "graph/node/serializer.h" #include "graph/node/node.h" +#include "graph/node/root.h" #include "graph/datum/datum.h" #include "graph/datum/types/eval_datum.h" @@ -16,106 +16,125 @@ // 3 -> 4: // Remove ShapeInputDatum (replace with ShapeDatum) // _name -> __name; _script -> __script -int SceneSerializer::PROTOCOL_VERSION = 4; +// 4 -> 5: +// Switch to plain-text. +int SceneSerializer::PROTOCOL_VERSION = 5; -SceneSerializer::SceneSerializer(QObject* node_root, +SceneSerializer::SceneSerializer(NodeRoot* node_root, QMap inspectors) : QObject(), node_root(node_root), inspectors(inspectors) { // Nothing to do here. } -QByteArray SceneSerializer::run(SerializerMode mode) +QJsonObject SceneSerializer::run(SerializerMode mode) { - QBuffer buffer; - buffer.open(QBuffer::WriteOnly); + QJsonObject out; + out["type"] = "sb"; + out["protocol"] = PROTOCOL_VERSION; - QDataStream stream(&buffer); - run(&stream, mode); - buffer.seek(0); + out["nodes"] = serializeNodes(node_root); - return buffer.data(); + if (mode == SERIALIZE_ALL) + out["connections"] = serializeConnections(); + return out; } -void SceneSerializer::run(QDataStream* out, SerializerMode mode) +QJsonArray SceneSerializer::serializeNodes(NodeRoot* r) { - *out << QString("sb") << quint32(PROTOCOL_VERSION); - serializeNodes(out, node_root); - - if (mode == SERIALIZE_NODES) - connections.clear(); - - serializeConnections(out); -} - -void SceneSerializer::serializeNodes(QDataStream* out, QObject* p) -{ - auto nodes = p->findChildren( + QJsonArray out; + nodes = r->findChildren( QString(), Qt::FindDirectChildrenOnly); - *out << quint32(nodes.length()); for (auto node : nodes) - serializeNode(out, node); + out.append(serializeNode(node)); + + return out; } -void SceneSerializer::serializeNode(QDataStream* out, Node* node) +QJsonObject SceneSerializer::serializeNode(Node* node) { - *out << quint32(0); // Dummy node type, since it doesn't exist anymore. - *out << node->objectName(); + QJsonObject out; - // Serialize position (or default QPointF if not provided) - *out << (inspectors.contains(node) ? inspectors[node] : QPointF()); + out["inspector"] = QJsonArray({ + inspectors[node].x(), + inspectors[node].y()}); + + QJsonArray datum_array; Datum* deferred = NULL; - auto datums = node->findChildren(QString(), - Qt::FindDirectChildrenOnly); - *out << quint32(datums.length()); + auto datums = node->findChildren( + QString(), Qt::FindDirectChildrenOnly); + for (auto d : datums) - { if (dynamic_cast(d)) - { - Q_ASSERT(deferred == NULL); deferred = d; - } else - { - serializeDatum(out, d); - } - } + datum_array.append(serializeDatum(d)); if (deferred) - serializeDatum(out, deferred); + datum_array.append(serializeDatum(deferred)); + + out["datums"] = datum_array; + + return out; } -void SceneSerializer::serializeDatum(QDataStream* out, Datum* datum) +QJsonObject SceneSerializer::serializeDatum(Datum* datum) { - *out << quint32(datum->getDatumType()); - *out << datum->objectName(); + QJsonObject out; + out["type"] = datum->getDatumTypeString(); + out["name"] = datum->objectName(); if (auto e = dynamic_cast(datum)) { - *out << e->getExpr(); + auto expr = e->getExpr(); + if (expr.contains("\n")) + { + auto a = QJsonArray(); + for (auto line : expr.split("\n")) + a.append(line); + out["expr"] = a; + } + else + { + out["expr"] = expr; + } } // Save datum and any connections for later // (as connections are serialized separately, // once all of the datums have been written). - datums << datum; for (auto d : datum->getInputDatums()) connections << QPair(d, datum); + + return out; } -void SceneSerializer::serializeConnections(QDataStream* out) +QJsonArray SceneSerializer::serializeConnections() { - // Only serialize connections for which we have serialized both datums - // (prevents edge cases in copy-paste) - QList> valid; + QJsonArray out; for (auto p : connections) - if (datums.contains(p.first) && datums.contains(p.second)) - valid << p; + { + QJsonArray start; + QJsonArray end; + + // Only serialize connections for which we have serialized + // both datums (prevents edge cases in copy-paste) + auto start_node = static_cast(p.first->parent()); + auto end_node = static_cast(p.second->parent()); + + if (nodes.contains(start_node) && nodes.contains(end_node)) + { + start.append(nodes.indexOf(start_node)); + start.append(p.first->objectName()); + + end.append(nodes.indexOf(end_node)); + end.append(p.second->objectName()); + + out.append(QJsonArray({start, end})); + } + } - *out << quint32(valid.length()); - for (auto p : valid) - *out << quint32(datums.indexOf(p.first)) - << quint32(datums.indexOf(p.second)); + return out; } diff --git a/src/graph/node/serializer.h b/src/graph/node/serializer.h index a4b2429e..6f424aec 100644 --- a/src/graph/node/serializer.h +++ b/src/graph/node/serializer.h @@ -6,33 +6,35 @@ #include #include #include +#include +#include class Datum; class Node; +class NodeRoot; class SceneSerializer : public QObject { Q_OBJECT public: explicit SceneSerializer( - QObject* node_root, + NodeRoot* node_root, QMap inspectors=QMap()); enum SerializerMode { SERIALIZE_ALL, SERIALIZE_NODES }; - void run(QDataStream* out, SerializerMode mode=SERIALIZE_ALL); - QByteArray run(SerializerMode mode=SERIALIZE_ALL); + QJsonObject run(SerializerMode mode=SERIALIZE_ALL); protected: - void serializeNodes(QDataStream* out, QObject* p); - void serializeNode(QDataStream* out, Node* node); - void serializeDatum(QDataStream* out, Datum* datum); - void serializeConnections(QDataStream* out); + QJsonArray serializeNodes(NodeRoot* r); + QJsonObject serializeNode(Node* node); + QJsonObject serializeDatum(Datum* datum); + QJsonArray serializeConnections(); - QObject* node_root; + NodeRoot* node_root; QMap inspectors; - QList datums; + QList nodes; QList> connections; static int PROTOCOL_VERSION; diff --git a/src/render/export_json.cpp b/src/render/export_json.cpp index 17c3a4bb..a290b86f 100644 --- a/src/render/export_json.cpp +++ b/src/render/export_json.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include "render/export_json.h" @@ -14,83 +16,61 @@ ExportJSONWorker::ExportJSONWorker(QMap s, QString filename, // Nothing to do here. } -void ExportJSONWorker::writeBounds(Shape* shape, QTextStream* out) +QJsonObject ExportJSONWorker::writeBounds(Shape* shape) { - *out << " \"bounds\": {\n"; - *out << " \"xmin\": " << shape->bounds.xmin << ",\n"; - *out << " \"ymin\": " << shape->bounds.ymin << ",\n"; - *out << " \"zmin\": "; - if (isinf(shape->bounds.zmin)) - { - *out << "null,\n"; - } - else - { - *out << shape->bounds.zmin << ",\n"; - } - *out << " \"xmax\": " << shape->bounds.xmax << ",\n"; - *out << " \"ymax\": " << shape->bounds.ymax << ",\n"; - *out << " \"zmax\": "; - if (isinf(shape->bounds.zmax)) - { - *out << "null\n"; - } - else - { - *out << shape->bounds.zmin << "\n"; - } - *out << " },\n"; + QJsonObject bounds; + bounds["xmin"] = shape->bounds.xmin; + bounds["ymin"] = shape->bounds.ymin; + bounds["zmin"] = isinf(shape->bounds.zmin) ? QJsonValue() + : shape->bounds.zmin; + bounds["xmax"] = shape->bounds.xmax; + bounds["ymax"] = shape->bounds.ymax; + bounds["zmax"] = isinf(shape->bounds.zmax) ? QJsonValue() + : shape->bounds.zmax; + return bounds; } -void ExportJSONWorker::writeColor(Shape* shape, QTextStream* out) +QJsonObject ExportJSONWorker::writeColor(Shape* shape) { - Q_UNUSED(shape); - // You can have any color you like, as long as it's white. - *out << " \"color\": {\n"; - *out << " \"R\": \"f255\",\n"; - *out << " \"G\": \"f255\",\n"; - *out << " \"B\": \"f255\"\n"; - *out << " }\n"; + QJsonObject color; + color["R"] = "f" + QString::number(shape->r > 0 ? shape->r : 255); + color["G"] = "f" + QString::number(shape->g > 0 ? shape->g : 255); + color["B"] = "f" + QString::number(shape->b > 0 ? shape->b : 255); + return color; } -void ExportJSONWorker::writeBody(Shape* shape, QTextStream* out) +QJsonObject ExportJSONWorker::writeBody(Shape* shape) { - *out << " \"body\": {\n"; + QJsonObject body; + if (format == EXPORT_JSON_INFIX) { - *out << " \"format\": \"infix\",\n"; - *out << " \"string\": \"" - << print_node_ss(shape->tree->head).c_str(); - } else if (format == EXPORT_JSON_PREFIX) { - *out << " \"format\": \"prefix\",\n"; - *out << " \"string\": \"" - << shape->math.c_str(); + body["format"] = "infix"; + body["string"] = print_node_ss(shape->tree->head).c_str(); } - - *out << "\"\n },\n"; + else if (format == EXPORT_JSON_PREFIX) + { + body["format"] = "prefix"; + body["string"] = shape->math.c_str(); + } + return body; } void ExportJSONWorker::run() { - QFile output_file(filename); - output_file.open(QFile::WriteOnly); - QTextStream out(&output_file); - - out << "{\n"; + QJsonObject out; for (auto s=shapes.begin(); s != shapes.end(); ++s) { - out << " \"" << s.key() << "\": {\n"; - writeBody(&s.value(), &out); - writeBounds(&s.value(), &out); - writeColor(&s.value(), &out); - out << " }"; - if (s + 1 != shapes.end()) - { - out << ","; - } - out << "\n"; + QJsonObject o; + o["body"] = writeBody(&s.value()); + o["bounds"] = writeBounds(&s.value()); + o["color"] = writeColor(&s.value()); + out[s.key()] = o; } - out << "}"; + + QFile output_file(filename); + output_file.open(QFile::WriteOnly); + output_file.write(QJsonDocument(out).toJson()); emit(finished()); } diff --git a/src/render/export_json.h b/src/render/export_json.h index 172c8113..a12e4e29 100644 --- a/src/render/export_json.h +++ b/src/render/export_json.h @@ -3,7 +3,7 @@ #include #include -#include +#include #include #include "fab/types/shape.h" @@ -22,9 +22,9 @@ public slots: signals: void finished(); protected: - void writeBounds(Shape* shape, QTextStream* out); - void writeBody(Shape* shape, QTextStream* out); - void writeColor(Shape* shape, QTextStream* out); + QJsonObject writeBounds(Shape* shape); + QJsonObject writeBody(Shape* shape); + QJsonObject writeColor(Shape* shape); QMap shapes; QString filename; diff --git a/src/render/render_task.cpp b/src/render/render_task.cpp index 785b55d8..84cd84db 100644 --- a/src/render/render_task.cpp +++ b/src/render/render_task.cpp @@ -24,6 +24,8 @@ RenderTask::RenderTask(PyObject *s, QMatrix4x4 matrix, RenderTask::~RenderTask() { + if (image) + delete image; Py_DECREF(shape); } @@ -46,7 +48,7 @@ DepthImageItem* RenderTask::getDepthImage(Viewport* viewport) Q_ASSERT(image); auto d = image->addToViewport(viewport); - image->deleteLater(); + delete image; image = NULL; return d; diff --git a/src/render/render_worker.cpp b/src/render/render_worker.cpp index ab997a89..703cc5dc 100644 --- a/src/render/render_worker.cpp +++ b/src/render/render_worker.cpp @@ -131,8 +131,7 @@ void RenderWorker::clearImage() { if (depth_image) { - depth_image->hide(); - depth_image->deleteAfterPaint(); + depth_image->deleteLater(); depth_image = NULL; } } diff --git a/src/ui/canvas/canvas.cpp b/src/ui/canvas/canvas.cpp index cb15a53b..d0654048 100644 --- a/src/ui/canvas/canvas.cpp +++ b/src/ui/canvas/canvas.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -223,8 +224,10 @@ void Canvas::onCopy() n->setParent(&temp_root); auto data = new QMimeData(); - data->setData("sb::canvas", SceneSerializer( - &temp_root, scene->inspectorPositions()).run()); + data->setData("sb::canvas", QJsonDocument( + SceneSerializer( + &temp_root, + scene->inspectorPositions()).run()).toJson()); QApplication::clipboard()->setMimeData(data); for (auto n : selected) @@ -255,7 +258,8 @@ void Canvas::onPaste() { NodeRoot temp_root; SceneDeserializer ds(&temp_root); - ds.run(data->data("sb::canvas")); + ds.run(QJsonDocument::fromJson( + data->data("sb::canvas")).object()); for (auto& i : ds.inspectors) i += QPointF(10, 10); diff --git a/src/ui/viewport/depth_image.cpp b/src/ui/viewport/depth_image.cpp index 57063e6f..e0a75b3f 100644 --- a/src/ui/viewport/depth_image.cpp +++ b/src/ui/viewport/depth_image.cpp @@ -14,7 +14,7 @@ DepthImageItem::DepthImageItem(QVector3D pos, QVector3D size, QImage depth, QImage shaded, QColor color, Viewport* viewport) : QGraphicsObject(), pos(pos), size(size), depth(depth), shaded(shaded), - color(color), viewport(viewport), delete_me(false) + color(color), viewport(viewport) { connect(viewport, &Viewport::viewChanged, this, &DepthImageItem::reposition); reposition(); @@ -85,20 +85,15 @@ QRectF DepthImageItem::boundingRect() const return QRectF(-sx/2, -sy/2, sx, sy); } -void DepthImageItem::deleteAfterPaint() -{ - delete_me = true; - prepareGeometryChange(); -} - void DepthImageItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { - Q_UNUSED(painter); Q_UNUSED(option); Q_UNUSED(widget); + painter->beginNativePainting(); + viewport->getQuadVertices()->bind(); // Find the parent MainWindow of our Viewport @@ -113,8 +108,7 @@ void DepthImageItem::paint(QPainter *painter, viewport->getQuadVertices()->release(); - if (delete_me) - deleteLater(); + painter->endNativePainting(); } void DepthImageItem::loadSharedShaderVariables(QOpenGLShaderProgram* shader) diff --git a/src/ui/viewport/depth_image.h b/src/ui/viewport/depth_image.h index 9aaa183a..e02df91a 100644 --- a/src/ui/viewport/depth_image.h +++ b/src/ui/viewport/depth_image.h @@ -21,11 +21,6 @@ class DepthImageItem : public QGraphicsObject, protected QOpenGLFunctions ~DepthImageItem(); QRectF boundingRect() const; - /* - * Calls prepareGeometryChange, then deleteLater after a paint event. - * This is used to clean up bounding boxes in the QGraphicsScene. - */ - void deleteAfterPaint(); /** Position of lower-left corner (in original scene units) */ const QVector3D pos; @@ -55,8 +50,6 @@ public slots: GLuint depth_tex; GLuint shaded_tex; - - bool delete_me; }; #endif // DEPTH_IMAGE_H diff --git a/src/ui/viewport/viewport.cpp b/src/ui/viewport/viewport.cpp index 62970aed..ba980547 100644 --- a/src/ui/viewport/viewport.cpp +++ b/src/ui/viewport/viewport.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -516,7 +517,8 @@ void Viewport::onCopy() NodeRoot temp_root; n->setParent(&temp_root); auto data = new QMimeData(); - data->setData("sb::viewport", SceneSerializer(&temp_root).run()); + data->setData("sb::viewport", + QJsonDocument(SceneSerializer(&temp_root).run()).toJson()); n->setParent(p); QApplication::clipboard()->setMimeData(data); @@ -536,7 +538,8 @@ void Viewport::onCut() NodeRoot temp_root; n->setParent(&temp_root); auto data = new QMimeData(); - data->setData("sb::viewport", SceneSerializer(&temp_root).run()); + data->setData("sb::viewport", + QJsonDocument(SceneSerializer(&temp_root).run()).toJson()); n->setParent(p); QApplication::clipboard()->setMimeData(data); @@ -552,7 +555,7 @@ void Viewport::onPaste() { NodeRoot temp_root; SceneDeserializer ds(&temp_root); - ds.run(data->data("sb::viewport")); + ds.run(QJsonDocument::fromJson(data->data("sb::viewport")).object()); auto n = temp_root.findChild(); n->setParent(App::instance()->getNodeRoot());