using OSCADSharp.Booleans;
using OSCADSharp.Spatial;
using OSCADSharp.Transforms;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OSCADSharp
{
///
/// Represents any Object or collection of objects that becomes am
/// an OpenSCAD script when converted to a string.
///
public abstract class OSCADObject
{
#region Attributes
private uint id = Ids.Get();
///
/// The unique Id of the object
/// these values auto-increment
///
public uint Id { get { return this.id; } }
///
/// Name of this OSCADObject
///
public string Name { get; set; } = null;
#endregion
#region Transforms
///
/// Applies Color and/or Opacity to this object
///
/// The name of the color to apply
/// The opacity from 0.0 to 1.0
/// A colorized object
public OSCADObject Color(string colorName, double opacity = 1.0)
{
return new ColoredObject(this, colorName, opacity);
}
///
/// Mirrors the object about a plane, as specified by the normal
///
/// The normal vector of the plane intersecting the origin of the object,
/// through which to mirror it.
/// A mirrored object
public OSCADObject Mirror(Vector3 normal)
{
return new MirroredObject(this, normal);
}
///
/// Mirrors the object about a plane, as specified by the normal
/// described by the x/y/z components provided
///
///
///
///
/// A mirrored object
public OSCADObject Mirror(double x, double y, double z)
{
return this.Mirror(new Vector3(x, y, z));
}
///
/// Resizes to a specified set of X/Y/Z dimensions
///
/// The X/Y/Z dimensions
/// A resized object
public OSCADObject Resize(Vector3 newsize)
{
return new ResizedObject(this, newsize);
}
///
/// Resizes to a specified set of X/Y/Z dimensions
///
///
///
///
/// A resized object
public OSCADObject Resize(double x, double y, double z)
{
return this.Resize(new Vector3(x, y, z));
}
///
/// Rotates about a specified X/Y/Z euler angle
///
/// The angle(s) to rotate
/// A rotated object
public OSCADObject Rotate(Vector3 angle)
{
return new RotatedObject(this, angle);
}
///
/// Rotates about a specified X/Y/Z euler angle
///
///
///
///
/// A rotated object
public OSCADObject Rotate(double x, double y, double z)
{
return this.Rotate(new Vector3(x, y, z));
}
///
/// Rescales an object by an X/Y/Z scale factor
///
/// The scale to apply. For example 1, 2, 1 would yield 2x scale on the Y axis
/// A scaled object
public OSCADObject Scale(Vector3 scale)
{
return new ScaledObject(this, scale);
}
///
/// Rescales an object by an X/Y/Z scale factor
///
///
///
///
/// A scaled object
public OSCADObject Scale(double x, double y, double z)
{
return this.Scale(new Vector3(x, y, z));
}
///
/// Translates an object by the specified amount
///
/// The vector upon which to translate (move object(s))
/// A translated object
public OSCADObject Translate(Vector3 translation)
{
return new TranslatedObject(this, translation);
}
///
/// Translates an object by the specified amount
///
///
///
///
/// A translated object
public OSCADObject Translate(double x, double y, double z)
{
return this.Translate(new Vector3(x, y, z));
}
///
/// Creates a minkowski sum of child nodes (including this object)
///
/// Nodes to sum with
/// A minkowski sum
public OSCADObject Minkowski(params OSCADObject[] objects)
{
return doBlockStatement("Minkowski", objects, (children) => { return new MinkowskiedObject(children); });
}
///
/// Creates a conved hull from child nodes (including this object)
///
/// Nodes to hull
/// Hull of nodes
public OSCADObject Hull(params OSCADObject[] objects)
{
return doBlockStatement("Hull", objects, (children) => { return new HulledObject(children); });
}
#endregion
#region Boolean Operations
///
/// Creates a union of all its child nodes. This is the sum of all children (logical or).
/// May be used with either 2D or 3D objects, but don't mix them.
///
/// child nodes
///
public OSCADObject Union(params OSCADObject[] objects)
{
return doBlockStatement("Union", objects, (children) => { return new Union(children); });
}
///
/// Subtracts the 2nd (and all further) child nodes from the first one (logical and not).
/// May be used with either 2D or 3D objects, but don't mix them.
///
/// child nodes
///
public OSCADObject Difference(params OSCADObject[] objects)
{
return doBlockStatement("Difference", objects, (children) => { return new Difference(children); });
}
///
/// Creates the intersection of all child nodes. This keeps the overlapping portion (logical and).
/// Only the area which is common or shared by all children is retained.
/// May be used with either 2D or 3D objects, but don't mix them.
///
/// child nodes
///
public OSCADObject Intersection(params OSCADObject[] objects)
{
return doBlockStatement("Intersection", objects, (children) => { return new Intersection(children); });
}
private OSCADObject doBlockStatement(string name, OSCADObject[] objects, Func, OSCADObject> factory)
{
if (objects == null || objects.Length < 1)
{
throw new ArgumentException(name + " requires at least one non-null entities");
}
IEnumerable children = new List() { this };
children = children.Concat(objects);
return factory(children);
}
#endregion
#region Utility Methods
///
/// Returns the computed position of this object.
///
/// For some objects that are the aggregate of many operations or
/// multiple children, this may be an approximation or average
/// of the position.
///
///
public abstract Vector3 Position();
///
/// Returns the approximate boundaries of this OpenSCAD object
///
///
public abstract Bounds Bounds();
///
/// Creates a copy of this object and all of its children
///
/// This is not a deep copy in the sense that all OSCADObjects will be new instances,
/// but any complex objects used as parameters (Such as Vector3s) will be referenced by the copies
///
/// Clone of this object
public abstract OSCADObject Clone();
///
/// Indicates whether this OSCADObject is the same as another OSCADObject.
/// This processes the scripts for both objects and is computationally expensive.
/// DO NOT use on deeply nested structures.
///
/// OSCADObject to compare to
/// True if scripts are exactly the same, false otherwise
public bool IsSameAs(OSCADObject other)
{
return this.ToString() == other.ToString();
}
///
/// Internal collection of children for this object
///
protected List children = new List();
///
/// Returns all chidren of this OSCADObject
///
/// If true, returns all descendants. If false, returns only direct children.
///
public IEnumerable Children(bool recursive = true)
{
if(recursive == false)
{
return new List(this.children);
}
// Initial children are reversed here because for objects with multiple children (such as boolean operations)
// the natural collection order would yield opposite the expected order in a stack (first child would be the last popped)
Stack toTraverse = new Stack(this.children.Reverse());
List allChildren = new List();
OSCADObject child = null;
while(toTraverse.Count > 0)
{
child = toTraverse.Pop();
allChildren.Add(child);
foreach (var subChild in child.children)
{
toTraverse.Push(subChild);
}
}
return allChildren;
}
///
/// Retrieves children that match the filtering predicate
///
/// An expression like Linq's .Where() clause
///
public IEnumerable Children(Func predicate)
{
return this.Children().Where(predicate);
}
///
/// Writes the script for this OSCADObject to the file specified
///
/// (This is just a shortcut for File.WriteAllLines)
///
/// Path for the file to write. Including filename and (optionally) file extension
public void ToFile(string filePath)
{
string path = filePath;
if (!path.EndsWith(".scad"))
{
path += ".scad";
}
File.WriteAllLines(path, new string[] { this.ToString() });
}
#endregion
#region Operators
///
/// Adds two OSCADObjects together (unions them)
///
///
///
///
public static OSCADObject operator +(OSCADObject left, OSCADObject right)
{
if(left.GetType() == typeof(Union))
{
left.children.Add(right);
return left;
}
else if(right.GetType() == typeof(Union))
{
right.children.Add(left);
return right;
}
else
{
return new Union(new OSCADObject[] {left, right });
}
}
///
/// Subtracts two OSCADObjects (differences them)
///
///
///
///
public static OSCADObject operator -(OSCADObject left, OSCADObject right)
{
if (left.GetType() == typeof(Difference))
{
left.children.Add(right);
return left;
}
else if (right.GetType() == typeof(Difference))
{
right.children.Add(left);
return right;
}
else
{
return new Difference(new OSCADObject[] {left, right });
}
}
#endregion
}
}