about summary refs log tree commit diff
path: root/nixpkgs/pkgs/applications/misc/cura
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/applications/misc/cura')
-rw-r--r--nixpkgs/pkgs/applications/misc/cura/default.nix65
-rw-r--r--nixpkgs/pkgs/applications/misc/cura/lulzbot/curaengine-openmp-compat.patch47
-rw-r--r--nixpkgs/pkgs/applications/misc/cura/lulzbot/curaengine.nix27
-rw-r--r--nixpkgs/pkgs/applications/misc/cura/lulzbot/default.nix82
-rw-r--r--nixpkgs/pkgs/applications/misc/cura/lulzbot/libarcus.nix33
-rw-r--r--nixpkgs/pkgs/applications/misc/cura/lulzbot/libsavitar.nix33
-rw-r--r--nixpkgs/pkgs/applications/misc/cura/lulzbot/uranium.nix38
-rw-r--r--nixpkgs/pkgs/applications/misc/cura/numpy-cast.patch12
-rw-r--r--nixpkgs/pkgs/applications/misc/cura/plugins.nix76
-rw-r--r--nixpkgs/pkgs/applications/misc/cura/stable.nix73
10 files changed, 486 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/applications/misc/cura/default.nix b/nixpkgs/pkgs/applications/misc/cura/default.nix
new file mode 100644
index 000000000000..a33cb7b2ce6c
--- /dev/null
+++ b/nixpkgs/pkgs/applications/misc/cura/default.nix
@@ -0,0 +1,65 @@
+{ mkDerivation, lib, fetchFromGitHub, cmake, python3, qtbase,
+ qtquickcontrols2, qtgraphicaleffects, curaengine, plugins ? [] }:
+
+mkDerivation rec {
+  pname = "cura";
+  version = "4.9.0";
+
+  src = fetchFromGitHub {
+    owner = "Ultimaker";
+    repo = "Cura";
+    rev = version;
+    sha256 = "1q515qwrzla3ikbsjmk91y0nrbwih11jycgmd50lkrmnkh7qj0r2";
+  };
+
+  materials = fetchFromGitHub {
+    owner = "Ultimaker";
+    repo = "fdm_materials";
+    rev = version;
+    sha256 = "0hi9w1fsnazlr0vvxdr3alsdb8m1vjjfp5zhmlz4kyyxhsy3bc33";
+  };
+
+  buildInputs = [ qtbase qtquickcontrols2 qtgraphicaleffects ];
+  propagatedBuildInputs = with python3.pkgs; [
+    libsavitar numpy-stl pyserial requests uranium zeroconf pynest2d
+    sentry-sdk trimesh keyring
+  ] ++ plugins;
+  nativeBuildInputs = [ cmake python3.pkgs.wrapPython ];
+
+  cmakeFlags = [
+    "-DURANIUM_DIR=${python3.pkgs.uranium.src}"
+    "-DCURA_VERSION=${version}"
+  ];
+
+  makeWrapperArgs = [
+    # hacky workaround for https://github.com/NixOS/nixpkgs/issues/59901
+    "--set OMP_NUM_THREADS 1"
+  ];
+
+  postPatch = ''
+    sed -i 's,/python''${PYTHON_VERSION_MAJOR}/dist-packages,/python''${PYTHON_VERSION_MAJOR}.''${PYTHON_VERSION_MINOR}/site-packages,g' CMakeLists.txt
+    sed -i 's, executable_name = .*, executable_name = "${curaengine}/bin/CuraEngine",' plugins/CuraEngineBackend/CuraEngineBackend.py
+  '';
+
+  postInstall = ''
+    mkdir -p $out/share/cura/resources/materials
+    cp ${materials}/*.fdm_material $out/share/cura/resources/materials/
+    mkdir -p $out/lib/cura/plugins
+    for plugin in ${toString plugins}; do
+      ln -s $plugin/lib/cura/plugins/* $out/lib/cura/plugins
+    done
+  '';
+
+  postFixup = ''
+    wrapPythonPrograms
+    wrapQtApp $out/bin/cura
+  '';
+
+  meta = with lib; {
+    description = "3D printer / slicing GUI built on top of the Uranium framework";
+    homepage = "https://github.com/Ultimaker/Cura";
+    license = licenses.lgpl3Plus;
+    platforms = platforms.linux;
+    maintainers = with maintainers; [ abbradar gebner ];
+  };
+}
diff --git a/nixpkgs/pkgs/applications/misc/cura/lulzbot/curaengine-openmp-compat.patch b/nixpkgs/pkgs/applications/misc/cura/lulzbot/curaengine-openmp-compat.patch
new file mode 100644
index 000000000000..3826e92440f0
--- /dev/null
+++ b/nixpkgs/pkgs/applications/misc/cura/lulzbot/curaengine-openmp-compat.patch
@@ -0,0 +1,47 @@
+# Notes by Charles Duffy <charles@dyfis.net> --
+#
+# - The new version of OpenMP does not allow outside variables to be referenced
+#   *at all* without an explicit declaration of how they're supposed to be
+#   handled. Thus, this was an outright build failure beforehand. The new
+#   pragmas copy the initial value from the outer scope into each parallel
+#   thread. Since these variables are all constant within the loops, this is
+#   clearly correct. (Not sure it's *optimal*, but quite sure it isn't
+#   *wrong*).
+# - Upstream has been contacted -- I'm a Lulzbot customer with an active
+#   support contract and sent them the patch. That said, they're in the middle
+#   of some major corporate churn (sold themselves out of near-bankruptcy to an
+#   out-of-state business entity formed as a holding company; moved to that
+#   state; have been slowly restaffing after), so a response may take a while.
+# - The patch is purely my own work.
+
+--- curaengine/src/support.cpp.orig	2020-03-28 10:38:01.953912363 -0500
++++ curaengine/src/support.cpp	2020-03-28 10:45:28.999791908 -0500
+@@ -854,7 +854,7 @@
+     const double tan_angle = tan(angle) - 0.01;  // the XY-component of the supportAngle
+     xy_disallowed_per_layer[0] = storage.getLayerOutlines(0, false).offset(xy_distance);
+     // for all other layers (of non support meshes) compute the overhang area and possibly use that when calculating the support disallowed area
+-    #pragma omp parallel for default(none) shared(xy_disallowed_per_layer, storage, mesh) schedule(dynamic)
++    #pragma omp parallel for default(none) firstprivate(layer_count, is_support_mesh_place_holder, use_xy_distance_overhang, z_distance_top, tan_angle, xy_distance, xy_distance_overhang) shared(xy_disallowed_per_layer, storage, mesh) schedule(dynamic)
+     for (unsigned int layer_idx = 1; layer_idx < layer_count; layer_idx++)
+     {
+         Polygons outlines = storage.getLayerOutlines(layer_idx, false);
+@@ -1054,7 +1054,7 @@
+         const int max_checking_layer_idx = std::min(static_cast<int>(storage.support.supportLayers.size())
+                                                   , static_cast<int>(layer_count - (layer_z_distance_top - 1)));
+         const size_t max_checking_idx_size_t = std::max(0, max_checking_layer_idx);
+-#pragma omp parallel for default(none) shared(support_areas, storage) schedule(dynamic)
++#pragma omp parallel for default(none) firstprivate(max_checking_idx_size_t, layer_z_distance_top) shared(support_areas, storage) schedule(dynamic)
+         for (size_t layer_idx = 0; layer_idx < max_checking_idx_size_t; layer_idx++)
+         {
+             support_areas[layer_idx] = support_areas[layer_idx].difference(storage.getLayerOutlines(layer_idx + layer_z_distance_top - 1, false));
+--- curaengine/src/layerPart.cpp.orig	2020-03-28 10:36:40.381023651 -0500
++++ curaengine/src/layerPart.cpp	2020-03-28 10:39:54.584140465 -0500
+@@ -49,7 +49,7 @@
+ {
+     const auto total_layers = slicer->layers.size();
+     assert(mesh.layers.size() == total_layers);
+-#pragma omp parallel for default(none) shared(mesh, slicer) schedule(dynamic)
++#pragma omp parallel for default(none) firstprivate(total_layers) shared(mesh, slicer) schedule(dynamic)
+     for (unsigned int layer_nr = 0; layer_nr < total_layers; layer_nr++)
+     {
+         SliceLayer& layer_storage = mesh.layers[layer_nr];
diff --git a/nixpkgs/pkgs/applications/misc/cura/lulzbot/curaengine.nix b/nixpkgs/pkgs/applications/misc/cura/lulzbot/curaengine.nix
new file mode 100644
index 000000000000..5d1df20e224b
--- /dev/null
+++ b/nixpkgs/pkgs/applications/misc/cura/lulzbot/curaengine.nix
@@ -0,0 +1,27 @@
+{ lib, gcc8Stdenv, callPackage, fetchgit, fetchpatch, cmake, libarcusLulzbot, stb, protobuf }:
+
+gcc8Stdenv.mkDerivation rec {
+  pname = "curaengine-lulzBot";
+  version = "3.6.21";
+
+  src = fetchgit {
+    url = "https://code.alephobjects.com/source/curaengine-lulzbot.git";
+    rev = "ec6a1a0f0aa387ef97e5c106633cf8d7fb9cd00d";
+    sha256 = "0wdkvg1hmqp1gaym804lw09x4ngf5ffasd861jhflpy7djbmkfn8";
+  };
+
+  patches = [ ./curaengine-openmp-compat.patch ];
+
+  nativeBuildInputs = [ cmake ];
+  buildInputs = [ libarcusLulzbot stb protobuf ];
+
+  cmakeFlags = [ "-DCURA_ENGINE_VERSION=${version}" ];
+
+  meta = with lib; {
+    description = "A powerful, fast and robust engine for processing 3D models into 3D printing instruction";
+    homepage = "https://code.alephobjects.com/source/curaengine-lulzbot/";
+    license = licenses.agpl3;
+    platforms = platforms.linux;
+    maintainers = with maintainers; [ chaduffy ];
+  };
+}
diff --git a/nixpkgs/pkgs/applications/misc/cura/lulzbot/default.nix b/nixpkgs/pkgs/applications/misc/cura/lulzbot/default.nix
new file mode 100644
index 000000000000..360ef47ecf1d
--- /dev/null
+++ b/nixpkgs/pkgs/applications/misc/cura/lulzbot/default.nix
@@ -0,0 +1,82 @@
+{ lib, mkDerivation, wrapQtAppsHook, callPackage, fetchgit, cmake, jq, python3, qtbase, qtquickcontrols2 }:
+
+let
+  # admittedly, we're using (printer firmware) blobs when we could compile them ourselves.
+  curaBinaryDataVersion = "3.6.21"; # Marlin v2.0.0.174 for Bio, v2.0.0.144 for others.
+  curaBinaryData = fetchgit {
+    url = "https://code.alephobjects.com/diffusion/CBD/cura-binary-data.git";
+    rev = "5c75d0f6c10d8b7a903e2072a48cd1f08059509e";
+    sha256 = "1qdsj6rczwzdwzyr7nz7fnypbphckjrnwl8c9dr6izsxyzs465c4";
+  };
+
+  libarcusLulzbot = callPackage ./libarcus.nix {
+    inherit (python3.pkgs) buildPythonPackage sip_4 pythonOlder;
+  };
+  libsavitarLulzbot = callPackage ./libsavitar.nix {
+    inherit (python3.pkgs) buildPythonPackage sip_4 pythonOlder;
+  };
+
+  inherit (python3.pkgs) buildPythonPackage pyqt5 numpy scipy shapely pythonOlder;
+  curaengine = callPackage ./curaengine.nix {
+    inherit libarcusLulzbot;
+  };
+  uraniumLulzbot = callPackage ./uranium.nix {
+    inherit callPackage libarcusLulzbot;
+    inherit (python3.pkgs) buildPythonPackage pyqt5 numpy scipy shapely pythonOlder;
+  };
+in
+mkDerivation rec {
+  pname = "cura-lulzbot";
+  version = "3.6.21";
+
+  src = fetchgit {
+    url = "https://code.alephobjects.com/source/cura-lulzbot.git";
+    rev = "7faeb18604c83004846a02c60cb240708db0034f";
+    sha256 = "10q38s8c8x6xkh1vns4p3iqa5y267vrjh5vq8h55mg1q5001scyq";
+  };
+
+  buildInputs = [ qtbase qtquickcontrols2 ];
+  # numpy-stl temporarily disabled due to https://code.alephobjects.com/T8415
+  propagatedBuildInputs = with python3.pkgs; [ pyserial requests zeroconf ] ++ [ libsavitarLulzbot uraniumLulzbot libarcusLulzbot ]; # numpy-stl
+  nativeBuildInputs = [ cmake python3.pkgs.wrapPython ];
+
+  cmakeFlags = [
+    "-DURANIUM_DIR=${uraniumLulzbot.src}"
+    "-DCURA_VERSION=${version}"
+  ];
+
+  postPatch = ''
+    sed -i 's,/python''${PYTHON_VERSION_MAJOR}/dist-packages,/python''${PYTHON_VERSION_MAJOR}.''${PYTHON_VERSION_MINOR}/site-packages,g' CMakeLists.txt
+    sed -i 's, executable_name = .*, executable_name = "${curaengine}/bin/CuraEngine",' plugins/CuraEngineBackend/CuraEngineBackend.py
+  '';
+
+  preFixup = ''
+    substituteInPlace "$out/bin/cura-lulzbot" --replace 'import cura.CuraApplication' 'import Savitar; import cura.CuraApplication'
+    ln -sT "${curaBinaryData}/cura/resources/firmware" "$out/share/cura/resources/firmware"
+    ln -sT "${uraniumLulzbot}/share/uranium" "$out/share/uranium"
+    ${jq}/bin/jq --arg out "$out" '.build=$out' >"$out/version.json" <<'EOF'
+    ${builtins.toJSON {
+      cura = version;
+      cura_version = version;
+      binarydata = curaBinaryDataVersion;
+      engine = curaengine.version;
+      libarcus = libarcusLulzbot.version;
+      libsavitar = libsavitarLulzbot.version;
+      uranium = uraniumLulzbot.version;
+    }}
+    EOF
+  '';
+
+  postFixup = ''
+    wrapPythonPrograms
+    wrapQtApp "$out/bin/cura-lulzbot"
+  '';
+
+  meta = with lib; {
+    description = "3D printer / slicing GUI built on top of the Uranium framework";
+    homepage = "https://code.alephobjects.com/diffusion/CURA/";
+    license = licenses.agpl3;  # a partial relicense to LGPL has happened, but not certain that all AGPL bits are expunged
+    platforms = platforms.linux;
+    maintainers = with maintainers; [ chaduffy ];
+  };
+}
diff --git a/nixpkgs/pkgs/applications/misc/cura/lulzbot/libarcus.nix b/nixpkgs/pkgs/applications/misc/cura/lulzbot/libarcus.nix
new file mode 100644
index 000000000000..15e221a8f743
--- /dev/null
+++ b/nixpkgs/pkgs/applications/misc/cura/lulzbot/libarcus.nix
@@ -0,0 +1,33 @@
+{ lib, buildPythonPackage, fetchgit, fetchurl, cmake, sip_4, protobuf, pythonOlder }:
+
+buildPythonPackage {
+  pname = "libarcus";
+  version = "3.6.21";
+  format = "other";
+
+  src = fetchgit {
+    url = "https://code.alephobjects.com/source/arcus.git";
+    rev = "aeda02d7727f45b657afb72cef203283fbf09325";
+    sha256 = "1ak0d4k745sx7paic27was3s4987z9h3czscjs21hxbi6qy83g99";
+  };
+
+  disabled = pythonOlder "3.4.0";
+
+  propagatedBuildInputs = [ sip_4 ];
+  nativeBuildInputs = [ cmake ];
+  buildInputs = [ protobuf ];
+
+  postPatch = ''
+    # To workaround buggy SIP detection which overrides PYTHONPATH
+    sed -i '/SET(ENV{PYTHONPATH}/d' cmake/FindSIP.cmake
+  '';
+
+  meta = with lib; {
+    description = "Communication library between internal components for Ultimaker software";
+    homepage = "https://code.alephobjects.com/source/arcus/";
+    license = licenses.lgpl3Plus;
+    platforms = platforms.linux;
+    maintainers = with maintainers; [ chaduffy ];
+  };
+}
+
diff --git a/nixpkgs/pkgs/applications/misc/cura/lulzbot/libsavitar.nix b/nixpkgs/pkgs/applications/misc/cura/lulzbot/libsavitar.nix
new file mode 100644
index 000000000000..dd84173ffce1
--- /dev/null
+++ b/nixpkgs/pkgs/applications/misc/cura/lulzbot/libsavitar.nix
@@ -0,0 +1,33 @@
+{ lib, buildPythonPackage, pythonOlder, fetchgit, cmake, sip_4 }:
+
+buildPythonPackage {
+  pname = "libsavitar-lulzbot";
+  name = "libsavitar-lulzbot";
+  version = "3.6.21";
+  format = "other";
+
+  src = fetchgit {
+    url = "https://code.alephobjects.com/source/savitar.git";
+    rev = "ee8ada42c55f54727ce4d275c294ba426d3d8234";
+    sha256 = "1wm5ii3cmni8dk3c65kw4wglpypkdsfpgd480d3hc1r5bqpq0d6j";
+  };
+
+  postPatch = ''
+    # To workaround buggy SIP detection which overrides PYTHONPATH
+    sed -i '/SET(ENV{PYTHONPATH}/d' cmake/FindSIP.cmake
+  '';
+
+  nativeBuildInputs = [ cmake ];
+
+  propagatedBuildInputs = [ sip_4 ];
+
+  disabled = pythonOlder "3.4.0";
+
+  meta = with lib; {
+    description = "C++ implementation of 3mf loading with SIP python bindings";
+    homepage = "https://github.com/Ultimaker/libSavitar";
+    license = licenses.lgpl3Plus;
+    platforms = platforms.unix;
+    maintainers = with maintainers; [ chaduffy ];
+  };
+}
diff --git a/nixpkgs/pkgs/applications/misc/cura/lulzbot/uranium.nix b/nixpkgs/pkgs/applications/misc/cura/lulzbot/uranium.nix
new file mode 100644
index 000000000000..01166092b2b5
--- /dev/null
+++ b/nixpkgs/pkgs/applications/misc/cura/lulzbot/uranium.nix
@@ -0,0 +1,38 @@
+{ lib, callPackage, fetchurl, fetchgit, buildPythonPackage, fetchFromGitHub, python, cmake
+, pyqt5, numpy, scipy, shapely, libarcusLulzbot, doxygen, gettext, pythonOlder }:
+
+buildPythonPackage {
+  version = "3.6.21";
+  pname = "uranium";
+  name = "uraniumLulzbot";
+  format = "other";
+
+  src = fetchgit {
+    url = "https://code.alephobjects.com/diffusion/U/uranium.git";
+    rev = "54d911edd2551c5875c554928896122835a0dd6c";
+    sha256 = "04bym3vwikaxw8ab0mymv9sc9n8i7yw5kfsv99ic811g9lzz3j1i";
+  };
+
+  disabled = pythonOlder "3.5.0";
+
+  buildInputs = [ python gettext ];
+  propagatedBuildInputs = [ pyqt5 numpy scipy shapely libarcusLulzbot ];
+  nativeBuildInputs = [ cmake doxygen ];
+
+  postPatch = ''
+    sed -i 's,/python''${PYTHON_VERSION_MAJOR}/dist-packages,/python''${PYTHON_VERSION_MAJOR}.''${PYTHON_VERSION_MINOR}/site-packages,g' CMakeLists.txt
+    sed -i \
+     -e "s,Resources.addSearchPath(os.path.join(os.path.abspath(os.path.dirname(__file__)).*,Resources.addSearchPath(\"$out/share/uranium/resources\")," \
+     -e "s,self._plugin_registry.addPluginLocation(os.path.join(os.path.abspath(os.path.dirname(__file__)).*,self._plugin_registry.addPluginLocation(\"$out/lib/uranium/plugins\")," \
+     UM/Application.py
+  '';
+
+  meta = with lib; {
+    description = "A Python framework for building Desktop applications";
+    homepage = "https://code.alephobjects.com/diffusion/U/";
+    license = licenses.lgpl3Plus;
+    platforms = platforms.linux;
+    maintainers = with maintainers; [ chaduffy ];
+  };
+}
+
diff --git a/nixpkgs/pkgs/applications/misc/cura/numpy-cast.patch b/nixpkgs/pkgs/applications/misc/cura/numpy-cast.patch
new file mode 100644
index 000000000000..efb14182b3e6
--- /dev/null
+++ b/nixpkgs/pkgs/applications/misc/cura/numpy-cast.patch
@@ -0,0 +1,12 @@
+diff -urN Cura-15.04.old/Cura/util/sliceEngine.py Cura-15.04/Cura/util/sliceEngine.py
+--- Cura-15.04.old/Cura/util/sliceEngine.py	2016-05-07 20:34:17.305020334 +0200
++++ Cura-15.04/Cura/util/sliceEngine.py	2016-05-07 20:40:02.993286467 +0200
+@@ -343,7 +343,7 @@
+ 						objMax[1] = max(oMax[1], objMax[1])
+ 			if objMin is None:
+ 				return
+-			pos += (objMin + objMax) / 2.0 * 1000
++			pos = numpy.add( pos, (objMin + objMax) / 2.0 * 1000, out=pos, casting='unsafe')
+ 			commandList += ['-s', 'posx=%d' % int(pos[0]), '-s', 'posy=%d' % int(pos[1])]
+ 
+ 			vertexTotal = [0] * 4
diff --git a/nixpkgs/pkgs/applications/misc/cura/plugins.nix b/nixpkgs/pkgs/applications/misc/cura/plugins.nix
new file mode 100644
index 000000000000..76a5808963f9
--- /dev/null
+++ b/nixpkgs/pkgs/applications/misc/cura/plugins.nix
@@ -0,0 +1,76 @@
+{ lib, stdenv, fetchFromGitHub, fetchpatch, python3Packages, libspnav, jq }:
+
+let
+
+  self = {
+
+    octoprint = stdenv.mkDerivation rec {
+      pname = "Cura-OctoPrintPlugin";
+      version = "3.5.18";
+
+      src = fetchFromGitHub {
+        owner = "fieldOfView";
+        repo = pname;
+        rev = "7bd73946fbf22d18337dc900a81a011ece26bee0";
+        sha256 = "057b2f5f49p96lkh2wsr9w6yh2003x4a85irqsgbzp6igmk8imdn";
+      };
+
+      propagatedBuildInputs = with python3Packages; [
+        netifaces
+      ];
+
+      installPhase = ''
+        mkdir -p $out/lib/cura/plugins/OctoPrintPlugin
+        cp -rv . $out/lib/cura/plugins/OctoPrintPlugin/
+      '';
+
+      meta = with lib; {
+        description = "Enables printing directly to OctoPrint and monitoring the process";
+        homepage = "https://github.com/fieldOfView/Cura-OctoPrintPlugin";
+        license = licenses.agpl3;
+        maintainers = with maintainers; [ gebner ];
+      };
+    };
+
+    rawmouse = stdenv.mkDerivation rec {
+      pname = "RawMouse";
+      version = "1.1.0";
+
+      src = fetchFromGitHub {
+        owner = "smartavionics";
+        repo = pname;
+        rev = version;
+        sha256 = "0hvi7qwd4xfnqnhbj9dgfjmvv9df7s42asf3fdfxv43n6nx74scw";
+      };
+
+      nativeBuildInputs = [ jq ];
+
+      propagatedBuildInputs = with python3Packages; [
+        hidapi
+      ];
+
+      buildPhase = ''
+        jq 'del(.devices) | .libspnav="${libspnav}/lib/libspnav.so"' \
+          <RawMouse/config.json >RawMouse/config.json.new
+        mv RawMouse/config.json.new RawMouse/config.json
+
+        # remove prebuilt binaries
+        rm -r RawMouse/hidapi
+      '';
+
+      installPhase = ''
+        mkdir -p $out/lib/cura/plugins/RawMouse
+        cp -rv . $out/lib/cura/plugins/RawMouse/
+      '';
+
+      meta = with lib; {
+        description = "Cura plugin for HID mice such as 3Dconnexion spacemouse";
+        homepage = "https://github.com/smartavionics/RawMouse";
+        license = licenses.agpl3Plus;
+        maintainers = with maintainers; [ gebner ];
+      };
+    };
+
+  };
+
+in self
diff --git a/nixpkgs/pkgs/applications/misc/cura/stable.nix b/nixpkgs/pkgs/applications/misc/cura/stable.nix
new file mode 100644
index 000000000000..5383cbe9a23a
--- /dev/null
+++ b/nixpkgs/pkgs/applications/misc/cura/stable.nix
@@ -0,0 +1,73 @@
+{ lib, stdenv, python27Packages, curaengine, makeDesktopItem, fetchurl }:
+let
+  py = python27Packages;
+  version = "15.04";
+in
+stdenv.mkDerivation rec {
+  pname = "cura";
+  inherit version;
+
+  src = fetchurl {
+    url = "https://github.com/daid/Cura/archive/${version}.tar.gz";
+    sha256 = "0xbjvzhp8wzq9lnpmcg1fjf7j5h39bj5463sd5c8jzdjl96izizl";
+  };
+
+  desktopItem = makeDesktopItem {
+    name = "Cura";
+    exec = "cura";
+    icon = "cura";
+    comment = "Cura";
+    desktopName = "Cura";
+    genericName = "3D printing host software";
+    categories = "GNOME;GTK;Utility;";
+  };
+
+  python_deps = with py; [ pyopengl pyserial numpy wxPython30 power setuptools ];
+
+  pythonPath = python_deps;
+
+  propagatedBuildInputs = python_deps;
+
+  buildInputs = [ curaengine py.wrapPython ];
+
+  configurePhase = "";
+  buildPhase = "";
+
+  patches = [ ./numpy-cast.patch ];
+
+  installPhase = ''
+    # Install Python code.
+    site_packages=$out/lib/python2.7/site-packages
+    mkdir -p $site_packages
+    cp -r Cura $site_packages/
+
+    # Install resources.
+    resources=$out/share/cura
+    mkdir -p $resources
+    cp -r resources/* $resources/
+    sed -i 's|os.path.join(os.path.dirname(__file__), "../../resources")|"'$resources'"|g' $site_packages/Cura/util/resources.py
+
+    # Install executable.
+    mkdir -p $out/bin
+    cp Cura/cura.py $out/bin/cura
+    chmod +x $out/bin/cura
+    sed -i 's|#!/usr/bin/python|#!/usr/bin/env python|' $out/bin/cura
+    wrapPythonPrograms
+
+    # Make it find CuraEngine.
+    echo "def getEngineFilename(): return '${curaengine}/bin/CuraEngine'" >> $site_packages/Cura/util/sliceEngine.py
+
+    # Install desktop item.
+    mkdir -p "$out"/share/applications
+    cp "$desktopItem"/share/applications/* "$out"/share/applications/
+    mkdir -p "$out"/share/icons
+    ln -s "$resources/images/c.png" "$out"/share/icons/cura.png
+  '';
+
+  meta = with lib; {
+    description = "3D printing host software";
+    homepage = "https://github.com/daid/Cura";
+    license = licenses.agpl3;
+    platforms = platforms.linux;
+  };
+}