diff --git a/OSCADSharp/OSCADSharp/OSCADSharp.csproj b/OSCADSharp/OSCADSharp/OSCADSharp.csproj
index 03e1da2..a2da1c7 100644
--- a/OSCADSharp/OSCADSharp/OSCADSharp.csproj
+++ b/OSCADSharp/OSCADSharp/OSCADSharp.csproj
@@ -49,6 +49,7 @@
+
diff --git a/OSCADSharp/OSCADSharp/Solids/Compound/Tube.cs b/OSCADSharp/OSCADSharp/Solids/Compound/Tube.cs
new file mode 100644
index 0000000..649d931
--- /dev/null
+++ b/OSCADSharp/OSCADSharp/Solids/Compound/Tube.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using OSCADSharp.DataBinding;
+using OSCADSharp.Spatial;
+
+namespace OSCADSharp.Solids.Compound
+{
+ ///
+ /// Represents a cylindrical container, by default it has a solid bottom and an open top.
+ ///
+ public class Tube : OSCADObject
+ {
+ #region Attributes
+ ///
+ /// Number of fragments in 360 degrees. Values of 3 or more override MinimumAngle and MinimumCircumferentialLength
+ /// ($fn in OpenSCAD)
+ ///
+ public int? Resolution { get; set; }
+
+ ///
+ /// The thickness of the container walls
+ ///
+ public double WallThickness { get; set; } = .1;
+
+ ///
+ /// Height of the cylinder or cone
+ ///
+ public double Height { get; set; } = 1;
+
+ ///
+ /// Radius of cylinder. r1 = r2 = r.
+ ///
+ public double Radius
+ {
+ get
+ {
+ return (Radius1 + Radius2) / 2;
+ }
+ set
+ {
+ this.Radius1 = value;
+ this.Radius2 = value;
+ }
+ }
+
+ ///
+ /// Radius, bottom of cone.
+ ///
+ public double Radius1 { get; set; } = 1;
+
+ ///
+ /// Radius, top of cone.
+ ///
+ public double Radius2 { get; set; } = 1;
+
+ ///
+ /// Diameter of cylinder. r1 = r2 = d /2.
+ ///
+ public double Diameter
+ {
+ get { return this.Radius * 2; }
+ set { this.Radius = value / 2; }
+ }
+
+ ///
+ /// Diameter, bottom of cone. r1 = d1 /2
+ ///
+ public double Diameter1
+ {
+ get { return this.Radius1 * 2; }
+ set { this.Radius1 = value / 2; }
+ }
+
+ ///
+ /// Diameter, top of cone. r2 = d2 /2
+ ///
+ public double Diameter2
+ {
+ get { return this.Radius2 * 2; }
+ set { this.Radius2 = value / 2; }
+ }
+
+ ///
+ /// If True, the center of the box will be at 0, 0, 0
+ ///
+ /// If False (default) one corner will be centered at 0,0, 0, with the cube extending into the positive octant (positive X/Y/Z)
+ ///
+ public bool Center { get; set; } = false;
+
+ ///
+ /// If true, the container has a solid bottom
+ ///
+ public bool Bottom { get; set; } = true;
+
+ ///
+ /// If true, the container has a solid top, otherwise the top is open
+ ///
+ public bool Top { get; set; } = false;
+ #endregion
+
+ #region Constructors
+ ///
+ /// Creates a tube with the default initialization values
+ ///
+ public Tube()
+ {
+ }
+ #endregion
+
+ #region Overrides
+ ///
+ /// Converts this object to an OpenSCAD script
+ ///
+ /// Script for this object
+ public override string ToString()
+ {
+ OSCADObject inner = new Cylinder() {
+ Diameter1 = this.Diameter1 - WallThickness * 2,
+ Diameter2 = this.Diameter2 - WallThickness * 2,
+ Height = this.Height - WallThickness * 2,
+ Center = this.Center,
+ Resolution = this.Resolution
+ };
+
+ if (!this.Bottom && !this.Top)
+ {
+ ((Cylinder)inner).Height += WallThickness * 4;
+ }
+ else if (!this.Top)
+ {
+ ((Cylinder)inner).Height += WallThickness * 2;
+ inner = inner.Translate(0, 0, WallThickness);
+ }
+ else if (!this.Bottom)
+ {
+ ((Cylinder)inner).Height += WallThickness * 2;
+ inner = inner.Translate(0, 0, -WallThickness);
+ }
+
+ OSCADObject cyl = new Cylinder()
+ {
+ Diameter1 = this.Diameter1,
+ Diameter2 = this.Diameter2,
+ Height = this.Height,
+ Center = this.Center,
+ Resolution = this.Resolution
+ } - inner;
+
+ return cyl.ToString();
+ }
+ ///
+ /// Binds a a variable to a property on this object
+ ///
+ /// A string specifying the property such as "Diameter" or "Radius"
+ /// The variable to bind the to. This variable will appear in script output in lieu of the
+ /// literal value of the property
+ public override void Bind(string property, Variable variable)
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// Returns the approximate boundaries of this OpenSCAD object
+ ///
+ ///
+ public override Bounds Bounds()
+ {
+ if (this.Center == false)
+ {
+ return new Bounds(new Vector3(-this.Radius, -this.Radius, 0),
+ new Vector3(this.Radius, this.Radius, this.Height));
+ }
+ else
+ {
+ return new Bounds(new Vector3(-this.Radius, -this.Radius, -this.Height / 2),
+ new Vector3(this.Radius, this.Radius, this.Height / 2));
+ }
+ }
+
+ ///
+ /// Gets a copy of this object that is a new instance
+ ///
+ ///
+ public override OSCADObject Clone()
+ {
+ return new Cylinder()
+ {
+ Name = this.Name,
+ Height = this.Height,
+ Radius1 = this.Radius1,
+ Radius2 = this.Radius2
+ };
+ }
+
+ ///
+ /// Gets the position of this object's center (origin) in
+ /// world space
+ ///
+ ///
+ public override Vector3 Position()
+ {
+ Vector3 position;
+ if (this.Center == false)
+ {
+ position = new Vector3(0, 0, this.Height / 2);
+ }
+ else
+ {
+ position = new Vector3();
+ }
+
+ return position;
+ }
+ #endregion
+ }
+}