# HG changeset patch
# User Teemu Piippo <crimsondusk64@gmail.com>
# Date 1455579546 -7200
# Node ID a2e4286adb0d1bb66286f8f1dfe2e7c9668ec6de
# Parent  49358df9495b6570a2812348eadc60f19675a334
Split PrimitivePrompt into its new source files, renamed it to GeneratePrimitivePrompt
Added PrimitiveSpec structure and used it to pass primitive specifications instead of 4 separate variables

diff -r 49358df9495b -r a2e4286adb0d CMakeLists.txt
--- a/CMakeLists.txt	Tue Feb 16 00:59:50 2016 +0200
+++ b/CMakeLists.txt	Tue Feb 16 01:39:06 2016 +0200
@@ -61,6 +61,7 @@
 	src/version.cpp
 	src/dialogs/colorselector.cpp
 	src/dialogs/configdialog.cpp
+	src/dialogs/generateprimitivedialog.cpp
 	src/dialogs/ldrawpathdialog.cpp
 	src/dialogs/newpartdialog.cpp
 	src/dialogs/openprogressdialog.cpp
@@ -68,7 +69,7 @@
 	src/editmodes/circleMode.cpp
 	src/editmodes/curvemode.cpp
 	src/editmodes/drawMode.cpp
-    src/editmodes/linePathMode.cpp
+	src/editmodes/linePathMode.cpp
 	src/editmodes/magicWandMode.cpp
 	src/editmodes/rectangleMode.cpp
 	src/editmodes/selectMode.cpp
@@ -115,6 +116,7 @@
 	src/transform.h
 	src/dialogs/colorselector.h
 	src/dialogs/configdialog.h
+	src/dialogs/generateprimitivedialog.h
 	src/dialogs/ldrawpathdialog.h
 	src/dialogs/newpartdialog.h
 	src/dialogs/openprogressdialog.h
@@ -145,7 +147,6 @@
 	ui/flip.ui
 	ui/intersector.ui
 	ui/isecalc.ui
-	ui/makeprim.ui
 	ui/overlay.ui
 	ui/rectifier.ui
 	ui/replcoords.ui
@@ -155,6 +156,7 @@
 	src/partdownloader.ui
 	src/dialogs/colorselector.ui
 	src/dialogs/configdialog.ui
+	src/dialogs/generateprimitivedialog.ui
 	src/dialogs/ldrawpathdialog.ui
 	src/dialogs/newpartdialog.ui
 	src/dialogs/openprogressdialog.ui
diff -r 49358df9495b -r a2e4286adb0d src/dialogs/generateprimitivedialog.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dialogs/generateprimitivedialog.cpp	Tue Feb 16 01:39:06 2016 +0200
@@ -0,0 +1,45 @@
+#include "../ldObject.h"
+#include "generateprimitivedialog.h"
+#include "ui_generateprimitivedialog.h"
+
+GeneratePrimitiveDialog::GeneratePrimitiveDialog (QWidget* parent, Qt::WindowFlags f) :
+	QDialog (parent, f),
+	ui(*new Ui_GeneratePrimitiveDialog)
+{
+	ui.setupUi (this);
+	connect (ui.highResolution, &QCheckBox::toggled, this, &GeneratePrimitiveDialog::highResolutionToggled);
+}
+
+
+GeneratePrimitiveDialog::~GeneratePrimitiveDialog()
+{
+	delete &ui;
+}
+
+
+void GeneratePrimitiveDialog::highResolutionToggled (bool on)
+{
+	ui.segments->setMaximum (on ? HighResolution : LowResolution);
+
+	// If the current value is 16 and we switch to hi-res, default the
+	// spinbox to 48. (should we scale this?)
+	if (on and ui.segments->value() == LowResolution)
+		ui.segments->setValue(HighResolution);
+}
+
+
+PrimitiveSpec GeneratePrimitiveDialog::spec() const
+{
+	PrimitiveSpec result;
+	result.type =
+		ui.typeCircle->isChecked()       ? Circle :
+		ui.typeCylinder->isChecked()     ? Cylinder :
+		ui.typeDisc->isChecked()         ? Disc :
+		ui.typeDiscNegative->isChecked() ? DiscNegative :
+		ui.typeRing->isChecked()         ? Ring :
+										   Cone;
+	result.divisions = ui.highResolution->isChecked() ? HighResolution : LowResolution;
+	result.segments = ui.segments->value();
+	result.ringNumber = ui.ringNumber->value();
+	return result;
+}
\ No newline at end of file
diff -r 49358df9495b -r a2e4286adb0d src/dialogs/generateprimitivedialog.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dialogs/generateprimitivedialog.h	Tue Feb 16 01:39:06 2016 +0200
@@ -0,0 +1,21 @@
+#pragma once
+#include <QDialog>
+#include "../primitives.h"
+
+class Ui_GeneratePrimitiveDialog;
+
+class GeneratePrimitiveDialog : public QDialog
+{
+	Q_OBJECT
+
+public:
+	GeneratePrimitiveDialog(QWidget* parent = nullptr, Qt::WindowFlags f = 0);
+	virtual ~GeneratePrimitiveDialog();
+	PrimitiveSpec spec() const;
+
+public slots:
+	void highResolutionToggled (bool on);
+
+private:
+	Ui_GeneratePrimitiveDialog& ui;
+};
diff -r 49358df9495b -r a2e4286adb0d src/dialogs/generateprimitivedialog.ui
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dialogs/generateprimitivedialog.ui	Tue Feb 16 01:39:06 2016 +0200
@@ -0,0 +1,309 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>GeneratePrimitiveDialog</class>
+ <widget class="QDialog" name="GeneratePrimitiveDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>413</width>
+    <height>169</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Generate Primitive</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0">
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout_6">
+       <item>
+        <widget class="QGroupBox" name="groupbox">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="title">
+          <string>Type</string>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_3">
+          <item row="1" column="0">
+           <widget class="QRadioButton" name="typeCircle">
+            <property name="text">
+             <string>Circle</string>
+            </property>
+            <property name="checked">
+             <bool>true</bool>
+            </property>
+            <property name="autoRepeat">
+             <bool>false</bool>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="1">
+           <widget class="QRadioButton" name="typeCylinder">
+            <property name="text">
+             <string>Cylinder</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="0">
+           <widget class="QRadioButton" name="typeDisc">
+            <property name="text">
+             <string>Disc</string>
+            </property>
+           </widget>
+          </item>
+          <item row="2" column="1">
+           <widget class="QRadioButton" name="typeDiscNegative">
+            <property name="text">
+             <string>Disc Negative</string>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="0">
+           <widget class="QRadioButton" name="typeRing">
+            <property name="text">
+             <string>Ring</string>
+            </property>
+           </widget>
+          </item>
+          <item row="3" column="1">
+           <widget class="QRadioButton" name="typeCone">
+            <property name="text">
+             <string>Cone</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QVBoxLayout" name="verticalLayout_2">
+       <item>
+        <spacer name="verticalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item>
+        <widget class="QCheckBox" name="highResolution">
+         <property name="text">
+          <string>High resolution (48)</string>
+         </property>
+        </widget>
+       </item>
+       <item>
+        <layout class="QGridLayout" name="gridLayout_2">
+         <item row="0" column="0">
+          <widget class="QLabel" name="label">
+           <property name="text">
+            <string>Segments:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QLabel" name="label_2">
+           <property name="text">
+            <string>Ring number:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1">
+          <widget class="QSpinBox" name="segments">
+           <property name="minimum">
+            <number>1</number>
+           </property>
+           <property name="maximum">
+            <number>16</number>
+           </property>
+           <property name="value">
+            <number>16</number>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="1">
+          <widget class="QSpinBox" name="ringNumber">
+           <property name="enabled">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item>
+        <spacer name="verticalSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>GeneratePrimitiveDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>254</x>
+     <y>140</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>146</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>GeneratePrimitiveDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>322</x>
+     <y>140</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>146</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>typeCircle</sender>
+   <signal>clicked(bool)</signal>
+   <receiver>ringNumber</receiver>
+   <slot>setDisabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>45</x>
+     <y>41</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>305</x>
+     <y>86</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>typeCylinder</sender>
+   <signal>clicked(bool)</signal>
+   <receiver>ringNumber</receiver>
+   <slot>setDisabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>109</x>
+     <y>42</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>287</x>
+     <y>86</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>typeDisc</sender>
+   <signal>clicked(bool)</signal>
+   <receiver>ringNumber</receiver>
+   <slot>setDisabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>49</x>
+     <y>66</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>287</x>
+     <y>87</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>typeDiscNegative</sender>
+   <signal>clicked(bool)</signal>
+   <receiver>ringNumber</receiver>
+   <slot>setDisabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>121</x>
+     <y>58</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>288</x>
+     <y>83</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>typeRing</sender>
+   <signal>clicked(bool)</signal>
+   <receiver>ringNumber</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>45</x>
+     <y>90</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>301</x>
+     <y>86</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>typeCone</sender>
+   <signal>clicked(bool)</signal>
+   <receiver>ringNumber</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>111</x>
+     <y>89</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>302</x>
+     <y>85</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+ <slots>
+  <slot>enableRingNumber()</slot>
+ </slots>
+</ui>
diff -r 49358df9495b -r a2e4286adb0d src/editmodes/circleMode.cpp
--- a/src/editmodes/circleMode.cpp	Tue Feb 16 00:59:50 2016 +0200
+++ b/src/editmodes/circleMode.cpp	Tue Feb 16 01:39:06 2016 +0200
@@ -77,8 +77,10 @@
 void CircleMode::buildCircle()
 {
 	LDObjectList objs;
-	const int segments (m_window->ringToolSegments());
-	const int divisions (m_window->ringToolHiRes() ? HighResolution : LowResolution);
+	PrimitiveSpec spec;
+	spec.segments = m_window->ringToolSegments();
+	spec.divisions = m_window->ringToolHiRes() ? HighResolution : LowResolution;
+	spec.ringNumber = 0;
 	double dist0 (getCircleDrawDist (0));
 	double dist1 (getCircleDrawDist (1));
 	LDDocument* refFile;
@@ -91,23 +93,28 @@
 	if (dist0 == dist1)
 	{
 		// If the radii are the same, there's no ring space to fill. Use a circle.
-		refFile = primitives()->getPrimitive(::Circle, segments, divisions, 0);
+		spec.type = ::Circle;
+		refFile = primitives()->getPrimitive(spec);
 		transform = getCircleDrawMatrix (dist0);
 		circleOrDisc = true;
 	}
 	else if (dist0 == 0 or dist1 == 0)
 	{
 		// If either radii is 0, use a disc.
-		refFile = primitives()->getPrimitive(::Disc, segments, divisions, 0);
+		spec.type = ::Disc;
+		refFile = primitives()->getPrimitive(spec);
 		transform = getCircleDrawMatrix ((dist0 != 0) ? dist0 : dist1);
 		circleOrDisc = true;
 	}
 	else if (g_RingFinder.findRings (dist0, dist1))
 	{
 		// The ring finder found a solution, use that. Add the component rings to the file.
+		spec.type = ::Ring;
+
 		for (const RingFinder::Component& cmp : g_RingFinder.bestSolution()->getComponents())
 		{
-			refFile = primitives()->getPrimitive(::Ring, segments, divisions, cmp.num);
+			spec.ringNumber = cmp.num;
+			refFile = primitives()->getPrimitive(spec);
 			LDSubfileReference* ref = LDSpawn<LDSubfileReference>();
 			ref->setFileInfo (refFile);
 			ref->setTransform (getCircleDrawMatrix (cmp.scale));
@@ -132,10 +139,10 @@
 		templ.setCoordinate (localz, renderer()->getDepthValue());
 
 		// Calculate circle coords
-		primitives()->makeCircle(segments, divisions, dist0, c0);
-		primitives()->makeCircle(segments, divisions, dist1, c1);
+		primitives()->makeCircle(spec.segments, spec.divisions, dist0, c0);
+		primitives()->makeCircle(spec.segments, spec.divisions, dist1, c1);
 
-		for (int i = 0; i < segments; ++i)
+		for (int i = 0; i < spec.segments; ++i)
 		{
 			Vertex v0, v1, v2, v3;
 			v0 = v1 = v2 = v3 = templ;
diff -r 49358df9495b -r a2e4286adb0d src/primitives.cpp
--- a/src/primitives.cpp	Tue Feb 16 00:59:50 2016 +0200
+++ b/src/primitives.cpp	Tue Feb 16 01:39:06 2016 +0200
@@ -28,18 +28,6 @@
 #include "ldpaths.h"
 #include "documentmanager.h"
 
-QList<PrimitiveCategory*> m_categories;
-
-static const QStringList g_radialNameRoots =
-{
-	"edge",
-	"cyli",
-	"disc",
-	"ndis",
-	"ring",
-	"con"
-};
-
 PrimitiveScanner* PrimitiveManager::activeScanner()
 {
 	return m_activeScanner;
@@ -409,22 +397,22 @@
 
 // =============================================================================
 //
-LDObjectList PrimitiveManager::makePrimitiveBody (PrimitiveType type, int segs, int divs, int num)
+LDObjectList PrimitiveManager::makePrimitiveBody(const PrimitiveSpec& spec)
 {
 	LDObjectList objs;
 	QList<int> conditionalLineSegments;
 	QList<QLineF> circle;
 
-	makeCircle (segs, divs, 1, circle);
+	makeCircle (spec.segments, spec.divisions, 1, circle);
 
-	for (int i = 0; i < segs; ++i)
+	for (int i = 0; i < spec.segments; ++i)
 	{
 		double x0 = circle[i].x1();
 		double x1 = circle[i].x2();
 		double z0 = circle[i].y1();
 		double z1 = circle[i].y2();
 
-		switch (type)
+		switch (spec.type)
 		{
 		case Circle:
 			{
@@ -446,7 +434,7 @@
 				double x2, x3, z2, z3;
 				double y0, y1, y2, y3;
 
-				if (type == Cylinder)
+				if (spec.type == Cylinder)
 				{
 					x2 = x1;
 					x3 = x0;
@@ -458,17 +446,17 @@
 				}
 				else
 				{
-					x2 = x1 * (num + 1);
-					x3 = x0 * (num + 1);
-					z2 = z1 * (num + 1);
-					z3 = z0 * (num + 1);
+					x2 = x1 * (spec.ringNumber + 1);
+					x3 = x0 * (spec.ringNumber + 1);
+					z2 = z1 * (spec.ringNumber + 1);
+					z3 = z0 * (spec.ringNumber + 1);
 
-					x0 *= num;
-					x1 *= num;
-					z0 *= num;
-					z1 *= num;
+					x0 *= spec.ringNumber;
+					x1 *= spec.ringNumber;
+					z0 *= spec.ringNumber;
+					z1 *= spec.ringNumber;
 
-					if (type == Ring)
+					if (spec.type == Ring)
 						y0 = y1 = y2 = y3 = 0.0f;
 					else
 					{
@@ -485,12 +473,12 @@
 				LDQuad* quad (LDSpawn<LDQuad> (v0, v1, v2, v3));
 				quad->setColor (MainColor);
 
-				if (type == Cylinder)
+				if (spec.type == Cylinder)
 					quad->invert();
 
 				objs << quad;
 
-				if (type == Cylinder or type == Cone)
+				if (spec.type == Cylinder or spec.type == Cone)
 					conditionalLineSegments << i;
 			}
 			break;
@@ -500,7 +488,7 @@
 			{
 				double x2, z2;
 
-				if (type == Disc)
+				if (spec.type == Disc)
 					x2 = z2 = 0.0f;
 				else
 				{
@@ -516,9 +504,9 @@
 				// they'll end up upside-down.
 				LDTriangle* seg (LDSpawn<LDTriangle>());
 				seg->setColor (MainColor);
-				seg->setVertex (type == Disc ? 0 : 2, v0);
+				seg->setVertex (spec.type == Disc ? 0 : 2, v0);
 				seg->setVertex (1, v1);
-				seg->setVertex (type == Disc ? 2 : 0, v2);
+				seg->setVertex (spec.type == Disc ? 2 : 0, v2);
 				objs << seg;
 			}
 			break;
@@ -527,26 +515,26 @@
 
 	// If this is not a full circle, we need a conditional line at the other
 	// end, too.
-	if (segs < divs and not conditionalLineSegments.isEmpty())
-		conditionalLineSegments << segs;
+	if (spec.segments < spec.divisions and not conditionalLineSegments.isEmpty())
+		conditionalLineSegments << spec.segments;
 
 	for (int i : conditionalLineSegments)
 	{
-		Vertex v0 (getRadialPoint (i, divs, cos), 0.0f, getRadialPoint (i, divs, sin));
+		Vertex v0 (getRadialPoint (i, spec.divisions, cos), 0.0f, getRadialPoint (i, spec.divisions, sin));
 		Vertex v1;
-		Vertex v2 (getRadialPoint (i + 1, divs, cos), 0.0f, getRadialPoint (i + 1, divs, sin));
-		Vertex v3 (getRadialPoint (i - 1, divs, cos), 0.0f, getRadialPoint (i - 1, divs, sin));
+		Vertex v2 (getRadialPoint (i + 1, spec.divisions, cos), 0.0f, getRadialPoint (i + 1, spec.divisions, sin));
+		Vertex v3 (getRadialPoint (i - 1, spec.divisions, cos), 0.0f, getRadialPoint (i - 1, spec.divisions, sin));
 
-		if (type == Cylinder)
+		if (spec.type == Cylinder)
 		{
 			v1 = Vertex (v0[X], 1.0f, v0[Z]);
 		}
-		else if (type == Cone)
+		else if (spec.type == Cone)
 		{
-			v1 = Vertex (v0[X] * (num + 1), 0.0f, v0[Z] * (num + 1));
-			v0.setX (v0.x() * num);
+			v1 = Vertex (v0[X] * (spec.ringNumber + 1), 0.0f, v0[Z] * (spec.ringNumber + 1));
+			v0.setX (v0.x() * spec.ringNumber);
 			v0.setY (1.0);
-			v0.setZ (v0.z() * num);
+			v0.setZ (v0.z() * spec.ringNumber);
 		}
 
 		LDCondLine* line = (LDSpawn<LDCondLine>());
@@ -576,10 +564,10 @@
 
 // =============================================================================
 //
-QString PrimitiveManager::makeRadialFileName (PrimitiveType type, int segs, int divs, int num)
+QString PrimitiveManager::makeRadialFileName(const PrimitiveSpec& spec)
 {
-	int numerator = segs;
-	int denominator = divs;
+	int numerator = spec.segments;
+	int denominator = spec.divisions;
 
 	// Simplify the fractional part, but the denominator must be at least 4.
 	simplify (numerator, denominator);
@@ -592,10 +580,11 @@
 	}
 
 	// Compose some general information: prefix, fraction, root, ring number
-	QString prefix = (divs == LowResolution) ? "" : format ("%1/", divs);
+	QString prefix = (spec.divisions == LowResolution) ? "" : format ("%1/", spec.divisions);
 	QString frac = format ("%1-%2", numerator, denominator);
-	QString root = g_radialNameRoots[type];
-	QString numstr = (type == Ring or type == Cone) ? format ("%1", num) : "";
+	static const char* roots[] = { "edge", "cyli", "disc", "ndis", "ring", "con" };
+	QString root = roots[spec.type];
+	QString numstr = (spec.type == Ring or spec.type == Cone) ? format ("%1", spec.ringNumber) : "";
 
 	// Truncate the root if necessary (7-16rin4.dat for instance).
 	// However, always keep the root at least 2 characters.
@@ -608,30 +597,30 @@
 
 // =============================================================================
 //
-LDDocument* PrimitiveManager::generatePrimitive (PrimitiveType type, int segments, int divisions, int number)
+LDDocument* PrimitiveManager::generatePrimitive(const PrimitiveSpec& spec)
 {
 	// Make the description
-	QString fraction = QString::number ((float) segments / divisions);
-	QString name = makeRadialFileName (type, segments, divisions, number);
+	QString fraction = QString::number ((float) spec.segments / spec.divisions);
+	QString name = makeRadialFileName(spec);
 	QString description;
 
 	// Ensure that there's decimals, even if they're 0.
 	if (fraction.indexOf (".") == -1)
 		fraction += ".0";
 
-	if (type == Ring or type == Cone)
+	if (spec.type == Ring or spec.type == Cone)
 	{
 		QString spacing =
-			(number < 10) ? "  " :
-			(number < 100) ? " "  : "";
+			(spec.ringNumber < 10) ? "  " :
+			(spec.ringNumber < 100) ? " "  : "";
 
-		description = format ("%1 %2%3 x %4", primitiveTypeName (type), spacing, number, fraction);
+		description = format ("%1 %2%3 x %4", primitiveTypeName (spec.type), spacing, spec.ringNumber, fraction);
 	}
 	else
-		description = format ("%1 %2", primitiveTypeName (type), fraction);
+		description = format ("%1 %2", primitiveTypeName (spec.type), fraction);
 
 	// Prepend "Hi-Res" if 48/ primitive.
-	if (divisions == HighResolution)
+	if (spec.divisions == HighResolution)
 		description.insert (0, "Hi-Res ");
 
 	LDDocument* document = m_window->newDocument();
@@ -639,6 +628,7 @@
 
 	QString author = APPNAME;
 	QString license = "";
+	bool hires = (spec.divisions == HighResolution);
 
 	if (not m_config->defaultName().isEmpty())
 	{
@@ -651,7 +641,7 @@
 	objs << LDSpawn<LDComment> (description)
 		 << LDSpawn<LDComment> (format ("Name: %1", name))
 		 << LDSpawn<LDComment> (format ("Author: %1", author))
-		 << LDSpawn<LDComment> (format ("!LDRAW_ORG Unofficial_%1Primitive", divisions == HighResolution ?  "48_" : ""))
+		 << LDSpawn<LDComment> (format ("!LDRAW_ORG Unofficial_%1Primitive", hires ?  "48_" : ""))
 		 << LDSpawn<LDComment> (license)
 		 << LDSpawn<LDEmpty>()
 		 << LDSpawn<LDBfc> (BfcStatement::CertifyCCW)
@@ -660,7 +650,7 @@
 	document->openForEditing();
 	document->history()->setIgnoring (false);
 	document->addObjects (objs);
-	document->addObjects (makePrimitiveBody (type, segments, divisions, number));
+	document->addObjects (makePrimitiveBody(spec));
 	document->addHistoryStep();
 	return document;
 }
@@ -670,44 +660,15 @@
 // Gets a primitive by the given specs. If the primitive cannot be found, it will
 // be automatically generated.
 //
-LDDocument* PrimitiveManager::getPrimitive (PrimitiveType type, int segs, int divs, int num)
+LDDocument* PrimitiveManager::getPrimitive(const PrimitiveSpec& spec)
 {
-	QString name = makeRadialFileName (type, segs, divs, num);
+	QString name = makeRadialFileName(spec);
 	LDDocument* document = m_window->documents()->getDocumentByName (name);
 
 	if (document)
 		return document;
 
-	return generatePrimitive (type, segs, divs, num);
-}
-
-// =============================================================================
-//
-PrimitivePrompt::PrimitivePrompt (QWidget* parent, Qt::WindowFlags f) :
-	QDialog (parent, f)
-{
-	ui = new Ui_MakePrimUI;
-	ui->setupUi (this);
-	connect (ui->cb_hires, SIGNAL (toggled (bool)), this, SLOT (hiResToggled (bool)));
-}
-
-// =============================================================================
-//
-PrimitivePrompt::~PrimitivePrompt()
-{
-	delete ui;
-}
-
-// =============================================================================
-//
-void PrimitivePrompt::hiResToggled (bool on)
-{
-	ui->sb_segs->setMaximum (on ? HighResolution : LowResolution);
-
-	// If the current value is 16 and we switch to hi-res, default the
-	// spinbox to 48.
-	if (on and ui->sb_segs->value() == LowResolution)
-		ui->sb_segs->setValue (HighResolution);
+	return generatePrimitive(spec);
 }
 
 // =============================================================================
diff -r 49358df9495b -r a2e4286adb0d src/primitives.h
--- a/src/primitives.h	Tue Feb 16 00:59:50 2016 +0200
+++ b/src/primitives.h	Tue Feb 16 01:39:06 2016 +0200
@@ -23,8 +23,9 @@
 #include "main.h"
 
 class LDDocument;
-class Ui_MakePrimUI;
+class Ui_GeneratePrimitiveDialog;
 class PrimitiveCategory;
+class PrimitiveScanner;
 
 struct Primitive
 {
@@ -33,6 +34,24 @@
 	PrimitiveCategory* category;
 };
 
+enum PrimitiveType
+{
+	Circle,
+	Cylinder,
+	Disc,
+	DiscNegative,
+	Ring,
+	Cone,
+};
+
+struct PrimitiveSpec
+{
+	PrimitiveType type;
+	int segments;
+	int divisions;
+	int ringNumber;
+};
+
 class PrimitiveCategory : public QObject
 {
 	Q_OBJECT
@@ -61,9 +80,39 @@
 	QString m_name;
 };
 
+class PrimitiveManager : public QObject, HierarchyElement
+{
+	Q_OBJECT
+
+public:
+	PrimitiveManager(QObject* parent);
+
+	PrimitiveScanner* activeScanner();
+	LDDocument* generatePrimitive(const PrimitiveSpec &spec);
+	LDDocument* getPrimitive(const PrimitiveSpec &spec);
+	QString getPrimitivesCfgPath() const;
+	void loadPrimitives();
+	void makeCircle(int segs, int divs, double radius, QList<QLineF>& lines);
+	QString makeRadialFileName(const PrimitiveSpec &spec);
+	void populateTreeWidget(QTreeWidget* tree, const QString& selectByDefault = QString());
+	QString primitiveTypeName(PrimitiveType type);
+	Q_SLOT void scanDone();
+	void startScan();
+
+private:
+	QList<PrimitiveCategory*> m_categories;
+	PrimitiveScanner* m_activeScanner;
+	QList<Primitive> m_primitives;
+	PrimitiveCategory* m_unmatched;
+
+	LDObjectList makePrimitiveBody(const PrimitiveSpec &spec);
+	void loadCategories();
+	void populateCategories();
+	void clearCategories();
+};
+
 //
-// Worker object that scans the primitives folder for primitives and
-// builds an index of them.
+// Worker object that scans the primitives folder for primitives and builds an index of them.
 //
 class PrimitiveScanner : public QObject, HierarchyElement
 {
@@ -89,62 +138,6 @@
 	int m_baselen;
 };
 
-extern QList<PrimitiveCategory*> m_categories;
-
-enum PrimitiveType
-{
-	Circle,
-	Cylinder,
-	Disc,
-	DiscNegative,
-	Ring,
-	Cone,
-};
-
-class PrimitivePrompt : public QDialog
-{
-	Q_OBJECT
-
-public:
-	explicit PrimitivePrompt (QWidget* parent = nullptr, Qt::WindowFlags f = 0);
-	virtual ~PrimitivePrompt();
-	Ui_MakePrimUI* ui;
-
-public slots:
-	void hiResToggled (bool on);
-};
-
-class PrimitiveManager : public QObject, HierarchyElement
-{
-	Q_OBJECT
-
-public:
-	PrimitiveManager(QObject* parent);
-
-	PrimitiveScanner* activeScanner();
-	LDDocument* generatePrimitive(PrimitiveType type, int segs, int divs, int num);
-	LDDocument* getPrimitive(PrimitiveType type, int segs, int divs, int num);
-	QString getPrimitivesCfgPath() const;
-	void loadPrimitives();
-	void makeCircle(int segs, int divs, double radius, QList<QLineF>& lines);
-	QString makeRadialFileName(PrimitiveType type, int segs, int divs, int num);
-	void populateTreeWidget(QTreeWidget* tree, const QString& selectByDefault = QString());
-	QString primitiveTypeName(PrimitiveType type);
-	Q_SLOT void scanDone();
-	void startScan();
-
-private:
-	QList<PrimitiveCategory*> m_categories;
-	PrimitiveScanner* m_activeScanner;
-	QList<Primitive> m_primitives;
-	PrimitiveCategory* m_unmatched;
-
-	LDObjectList makePrimitiveBody (PrimitiveType type, int segs, int divs, int num);
-	void loadCategories();
-	void populateCategories();
-	void clearCategories();
-};
-
 class PrimitiveTreeItem : public QTreeWidgetItem
 {
 public:
diff -r 49358df9495b -r a2e4286adb0d src/toolsets/filetoolset.cpp
--- a/src/toolsets/filetoolset.cpp	Tue Feb 16 00:59:50 2016 +0200
+++ b/src/toolsets/filetoolset.cpp	Tue Feb 16 01:39:06 2016 +0200
@@ -27,6 +27,7 @@
 #include "../dialogs/configdialog.h"
 #include "../dialogs/ldrawpathdialog.h"
 #include "../dialogs/newpartdialog.h"
+#include "../dialogs/generateprimitivedialog.h"
 #include "../documentmanager.h"
 #include "filetoolset.h"
 #include "ui_makeprim.h"
@@ -200,23 +201,12 @@
 
 void FileToolset::makePrimitive()
 {
-	PrimitivePrompt* dialog = new PrimitivePrompt (m_window);
+	GeneratePrimitiveDialog* dialog = new GeneratePrimitiveDialog(m_window);
 
 	if (not dialog->exec())
 		return;
 
-	int segs = dialog->ui->sb_segs->value();
-	int divs = dialog->ui->cb_hires->isChecked() ? HighResolution : LowResolution;
-	int num = dialog->ui->sb_ringnum->value();
-
-	PrimitiveType type =
-		dialog->ui->rb_circle->isChecked()   ? Circle :
-		dialog->ui->rb_cylinder->isChecked() ? Cylinder :
-		dialog->ui->rb_disc->isChecked()     ? Disc :
-		dialog->ui->rb_ndisc->isChecked()    ? DiscNegative :
-		dialog->ui->rb_ring->isChecked()     ? Ring : Cone;
-
-	LDDocument* primitive = primitives()->generatePrimitive(type, segs, divs, num);
+	LDDocument* primitive = primitives()->generatePrimitive(dialog->spec());
 	primitive->openForEditing();
 	m_window->save(primitive, false);
 }
diff -r 49358df9495b -r a2e4286adb0d ui/makeprim.ui
--- a/ui/makeprim.ui	Tue Feb 16 00:59:50 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,309 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MakePrimUI</class>
- <widget class="QDialog" name="MakePrimUI">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>336</width>
-    <height>147</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>Generate a Primitive</string>
-  </property>
-  <layout class="QVBoxLayout" name="verticalLayout">
-   <item>
-    <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0">
-     <item>
-      <layout class="QVBoxLayout" name="verticalLayout_6">
-       <item>
-        <widget class="QGroupBox" name="gb_type">
-         <property name="sizePolicy">
-          <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-           <horstretch>0</horstretch>
-           <verstretch>0</verstretch>
-          </sizepolicy>
-         </property>
-         <property name="title">
-          <string>Type</string>
-         </property>
-         <layout class="QGridLayout" name="gridLayout_3">
-          <item row="1" column="0">
-           <widget class="QRadioButton" name="rb_circle">
-            <property name="text">
-             <string>Circle</string>
-            </property>
-            <property name="checked">
-             <bool>true</bool>
-            </property>
-            <property name="autoRepeat">
-             <bool>false</bool>
-            </property>
-           </widget>
-          </item>
-          <item row="1" column="1">
-           <widget class="QRadioButton" name="rb_cylinder">
-            <property name="text">
-             <string>Cylinder</string>
-            </property>
-           </widget>
-          </item>
-          <item row="2" column="0">
-           <widget class="QRadioButton" name="rb_disc">
-            <property name="text">
-             <string>Disc</string>
-            </property>
-           </widget>
-          </item>
-          <item row="2" column="1">
-           <widget class="QRadioButton" name="rb_ndisc">
-            <property name="text">
-             <string>Disc Negative</string>
-            </property>
-           </widget>
-          </item>
-          <item row="3" column="0">
-           <widget class="QRadioButton" name="rb_ring">
-            <property name="text">
-             <string>Ring</string>
-            </property>
-           </widget>
-          </item>
-          <item row="3" column="1">
-           <widget class="QRadioButton" name="rb_cone">
-            <property name="text">
-             <string>Cone</string>
-            </property>
-           </widget>
-          </item>
-         </layout>
-        </widget>
-       </item>
-      </layout>
-     </item>
-     <item>
-      <layout class="QVBoxLayout" name="verticalLayout_2">
-       <item>
-        <spacer name="verticalSpacer_2">
-         <property name="orientation">
-          <enum>Qt::Vertical</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-          <size>
-           <width>20</width>
-           <height>40</height>
-          </size>
-         </property>
-        </spacer>
-       </item>
-       <item>
-        <widget class="QCheckBox" name="cb_hires">
-         <property name="text">
-          <string>Hi-res</string>
-         </property>
-        </widget>
-       </item>
-       <item>
-        <layout class="QGridLayout" name="gridLayout_2">
-         <item row="0" column="0">
-          <widget class="QLabel" name="label">
-           <property name="text">
-            <string>Segments:</string>
-           </property>
-          </widget>
-         </item>
-         <item row="1" column="0">
-          <widget class="QLabel" name="label_2">
-           <property name="text">
-            <string>Ring number:</string>
-           </property>
-          </widget>
-         </item>
-         <item row="0" column="1">
-          <widget class="QSpinBox" name="sb_segs">
-           <property name="minimum">
-            <number>1</number>
-           </property>
-           <property name="maximum">
-            <number>16</number>
-           </property>
-           <property name="value">
-            <number>16</number>
-           </property>
-          </widget>
-         </item>
-         <item row="1" column="1">
-          <widget class="QSpinBox" name="sb_ringnum">
-           <property name="enabled">
-            <bool>false</bool>
-           </property>
-          </widget>
-         </item>
-        </layout>
-       </item>
-       <item>
-        <spacer name="verticalSpacer">
-         <property name="orientation">
-          <enum>Qt::Vertical</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-          <size>
-           <width>20</width>
-           <height>40</height>
-          </size>
-         </property>
-        </spacer>
-       </item>
-      </layout>
-     </item>
-    </layout>
-   </item>
-   <item>
-    <widget class="QDialogButtonBox" name="buttonBox">
-     <property name="orientation">
-      <enum>Qt::Horizontal</enum>
-     </property>
-     <property name="standardButtons">
-      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
-     </property>
-    </widget>
-   </item>
-  </layout>
- </widget>
- <resources/>
- <connections>
-  <connection>
-   <sender>buttonBox</sender>
-   <signal>accepted()</signal>
-   <receiver>MakePrimUI</receiver>
-   <slot>accept()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>254</x>
-     <y>140</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>157</x>
-     <y>146</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>buttonBox</sender>
-   <signal>rejected()</signal>
-   <receiver>MakePrimUI</receiver>
-   <slot>reject()</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>322</x>
-     <y>140</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>286</x>
-     <y>146</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>rb_circle</sender>
-   <signal>clicked(bool)</signal>
-   <receiver>sb_ringnum</receiver>
-   <slot>setDisabled(bool)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>45</x>
-     <y>41</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>305</x>
-     <y>86</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>rb_cylinder</sender>
-   <signal>clicked(bool)</signal>
-   <receiver>sb_ringnum</receiver>
-   <slot>setDisabled(bool)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>109</x>
-     <y>42</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>287</x>
-     <y>86</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>rb_disc</sender>
-   <signal>clicked(bool)</signal>
-   <receiver>sb_ringnum</receiver>
-   <slot>setDisabled(bool)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>49</x>
-     <y>66</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>287</x>
-     <y>87</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>rb_ndisc</sender>
-   <signal>clicked(bool)</signal>
-   <receiver>sb_ringnum</receiver>
-   <slot>setDisabled(bool)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>121</x>
-     <y>58</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>288</x>
-     <y>83</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>rb_ring</sender>
-   <signal>clicked(bool)</signal>
-   <receiver>sb_ringnum</receiver>
-   <slot>setEnabled(bool)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>45</x>
-     <y>90</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>301</x>
-     <y>86</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>rb_cone</sender>
-   <signal>clicked(bool)</signal>
-   <receiver>sb_ringnum</receiver>
-   <slot>setEnabled(bool)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>111</x>
-     <y>89</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>302</x>
-     <y>85</y>
-    </hint>
-   </hints>
-  </connection>
- </connections>
- <slots>
-  <slot>enableRingNumber()</slot>
- </slots>
-</ui>