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());