package org.reprap.geometry.polygons;
import java.util.ArrayList;
import java.util.List;
import org.reprap.Attributes;
import org.reprap.Extruder;
import org.reprap.geometry.LayerRules;
import org.reprap.utilities.Debug;
/**
* Class to hold a list of BooleanGrids with associated atributes for each
* @author ensab
*
*/
public class BooleanGridList
{
private List<BooleanGrid> shapes = null;
public BooleanGridList()
{
shapes = new ArrayList<BooleanGrid>();
}
/**
* Deep copy
* @param a
*/
public BooleanGridList(BooleanGridList a)
{
shapes = new ArrayList<BooleanGrid>();
if(a == null) return;
for(int i = 0; i < a.size(); i++)
shapes.add(new BooleanGrid(a.get(i)));
}
/**
* Return the ith shape
* @param i
* @return
*/
public BooleanGrid get(int i)
{
return shapes.get(i);
}
/**
* Is a point in any of the shapes?
* @param p
* @return
*/
public boolean membership(Point2D p)
{
for(int i = 0; i < size(); i++)
if(get(i).get(p))
return true;
return false;
}
/**
* Return the ith attribute
* @param i
* @return
*/
public Attributes attribute(int i)
{
return shapes.get(i).attribute();
}
/**
* How many shapes are there in the list?
* @return
*/
public int size()
{
return shapes.size();
}
/**
* Remove an entry and close the gap
* @param i
*/
public void remove(int i)
{
shapes.remove(i);
}
/**
* Add a shape on the end
* @param p
*/
public void add(BooleanGrid b)
{
if(b == null)
{
Debug.e("BooleanGridList.add(): attempt to add null BooleanGrid.");
return;
}
if(b != BooleanGrid.nullBooleanGrid())
shapes.add(b);
}
/**
* Add another list of shapes on the end
* @param a
*/
public void add(BooleanGridList aa)
{
for(int i = 0; i < aa.size(); i++)
add(aa.get(i));
}
/**
* Reverse the order of the list
* @return
*/
public BooleanGridList reverse()
{
BooleanGridList result = new BooleanGridList();
for(int i = size() - 1; i >= 0; i--)
result.add(get(i));
return result;
}
/**
* Offset all the shapes in the list for this layer
* @param lc
* @param outline
* @param multiplier
* @return
*/
public BooleanGridList offset(LayerRules lc, boolean outline, double multiplier)
{
boolean foundation = lc.getLayingSupport();
if(outline && foundation)
Debug.e("Offsetting a foundation outline!");
BooleanGridList result = new BooleanGridList();
for(int i = 0; i < size(); i++)
{
Attributes att = attribute(i);
if(att == null)
Debug.e("BooleanGridList.offset(): null attribute!");
else
{
Extruder [] es = lc.getPrinter().getExtruders();
Extruder e;
int shells;
if(foundation)
{
e = es[0]; // By convention extruder 0 builds the foundation
shells = 1;
} else
{
e = att.getExtruder();
shells = e.getShells();
}
if(outline)
{
int shell = 0;
boolean carryOn = true;
while(carryOn && shell < shells)
{
double d = multiplier*((double)shell + 0.5)*e.getExtrusionSize();
BooleanGrid thisOne = get(i).offset(d);
if(thisOne.isEmpty())
carryOn = false;
else
{
if(shell == 0 && e.getSingleLine())
{
BooleanGrid lines = get(i).lines(thisOne, d);
lines.setThin(true);
result.add(lines);
}
result.add(thisOne);
}
shell++;
}
result = result.reverse(); // Best to plot from the inside out
} else
{
// Must be a hatch. Only do it if the gap is +ve or we're building the foundation
double offSize;
int ei = e.getInfillExtruderNumber();
Extruder ife = e;
if(ei >= 0)
ife = es[ei];
if(foundation)
offSize = 3;
else if(multiplier < 0)
offSize = multiplier*((double)shells + 0.5)*e.getExtrusionSize() + ife.getInfillOverlap();
else
offSize = multiplier*((double)shells + 0.5)*e.getExtrusionSize();
if (e.getExtrusionInfillWidth() > 0 || foundation) // Z value doesn't matter here
result.add(get(i).offset(offSize));
}
}
}
return result;
}
/**
* Work out all the polygons forming a set of borders
* @return
*/
public PolygonList borders()
{
PolygonList result = new PolygonList();
for(int i = 0; i < size(); i++)
result.add(get(i).allPerimiters(attribute(i)));
return result;
}
/**
* Work out all the open polygons forming a set of infill hatches. If surface
* is true, these polygons are on the outside (top or bottom). If it's false
* they are in the interior. If overrideDirection is not null, that is used as the hatch
* direction. Otherwise the hatch is provided by layerConditions.
* @param layerConditions
* @param surface
* @param overrideDirection
* @return
*/
public PolygonList hatch(LayerRules layerConditions, boolean surface, HalfPlane overrideDirection, boolean support) //, Rr2Point startNearHere)
{
PolygonList result = new PolygonList();
boolean foundation = layerConditions.getLayingSupport();
Extruder [] es = layerConditions.getPrinter().getExtruders();
for(int i = 0; i < size(); i++)
{
Extruder e;
Attributes att = attribute(i);
if(foundation)
e = es[0]; // Extruder 0 is used for foundations
else
e = att.getExtruder();
Extruder ei;
if(!surface)
{
ei = e.getInfillExtruder();
if(ei != null)
att = new Attributes(ei.getMaterial(), null, null, ei.getAppearance());
} else
ei = e;
if(ei != null)
{
HalfPlane hatchLine;
if(overrideDirection != null)
hatchLine = overrideDirection;
else
hatchLine = layerConditions.getHatchDirection(ei, support);
result.add(get(i).hatch(hatchLine, layerConditions.getHatchWidth(ei), att));
}
}
return result;
}
/**
* Run through the list, unioning entries in it that share the same material so that
* the result has just one entry per material.
*/
public BooleanGridList unionDuplicates()
{
BooleanGridList result = new BooleanGridList();
if(size() <= 0)
return result;
if(size() == 1)
return this;
boolean[] usedUp = new boolean[size()];
for(int i = 0; i < usedUp.length; i++)
usedUp[i] = false;
for(int i = 0; i < size() - 1; i++)
{
if(!usedUp[i])
{
BooleanGrid union = get(i);
int iExId = union.attribute().getExtruder().getID();
for(int j = i+1; j < size(); j++)
{
if(!usedUp[j])
{
BooleanGrid jg = get(j);
if(iExId == jg.attribute().getExtruder().getID())
{
union = BooleanGrid.union(union, jg);
usedUp[j] = true;
}
}
}
result.add(union);
}
}
if(!usedUp[size() - 1])
result.add(get(size() - 1));
return result;
}
/**
* Return a list of unions between the entries in a and b.
* Only pairs with the same extruder are unioned. If an element
* of a has no corresponding element in b, or vice versa, then
* those elements are returned unmodified in the result.
* @param a
* @param b
* @return
*/
public static BooleanGridList unions(BooleanGridList a, BooleanGridList b)
{
BooleanGridList result = new BooleanGridList();
if(a == null)
return b;
if(b == null)
return a;
if(a == b)
return a;
if(a.size() <= 0)
return b;
if(b.size() <= 0)
return a;
boolean[] bMatched = new boolean[b.size()];
for(int i = 0; i < bMatched.length; i++)
bMatched[i] = false;
for(int i = 0; i < a.size(); i++)
{
BooleanGrid abg = a.get(i);
boolean aMatched = false;
for(int j = 0; j < b.size(); j++)
{
if(abg.attribute().getExtruder().getID() == b.attribute(j).getExtruder().getID())
{
result.add(BooleanGrid.union(abg, b.get(j)));
bMatched[j] = true;
aMatched = true;
break;
}
}
if(!aMatched)
result.add(abg);
}
for(int i = 0; i < bMatched.length; i++)
if(!bMatched[i])
result.add(b.get(i));
return result.unionDuplicates();
}
/**
* Return a list of intersections between the entries in a and b.
* Only pairs with the same extruder are intersected. If an element
* of a has no corresponding element in b, or vice versa, then no entry is returned
* for them.
* @param a
* @param b
* @return
*/
public static BooleanGridList intersections(BooleanGridList a, BooleanGridList b)
{
BooleanGridList result = new BooleanGridList();
if(a == null || b == null)
return result;
if(a == b)
return a;
if(a.size() <= 0 || b.size() <= 0)
return result;
for(int i = 0; i < a.size(); i++)
{
BooleanGrid abg = a.get(i);
for(int j = 0; j < b.size(); j++)
{
if(abg.attribute().getExtruder().getID() == b.attribute(j).getExtruder().getID())
{
result.add(BooleanGrid.intersection(abg, b.get(j)));
break;
}
}
}
return result.unionDuplicates();
}
/**
* Return only those elements in the list that have no support material specified
* @return
*/
public BooleanGridList cullNoSupport()
{
BooleanGridList result = new BooleanGridList();
for(int i = 0; i < size(); i++)
if(get(i).attribute().getExtruder().getSupportExtruderNumber() < 0)
result.add(get(i));
return result;
}
/**
* Return only those elements in the list that have support material specified
* @return
*/
public BooleanGridList cullSupport()
{
BooleanGridList result = new BooleanGridList();
for(int i = 0; i < size(); i++)
if(get(i).attribute().getExtruder().getSupportExtruderNumber() >= 0)
result.add(get(i));
return result;
}
/**
* Return a list of differences between the entries in a and b.
* Only pairs with the same attribute are subtracted. If an element
* of a has no corresponding element in b, then an entry equal to a is returned
* for that.
*
* If onlyNullSupport is true then only entries in a with support equal to null are
* considered. Otherwise ordinary set difference is returned.
*
* @param a
* @param b
* @param onlyNullSupport
* @return
*/
public static BooleanGridList differences(BooleanGridList a, BooleanGridList b)
{
BooleanGridList result = new BooleanGridList();
if(a == null)
return result;
if(b == null)
return a;
if(a == b)
return result;
if(a.size() <= 0)
return result;
if(b.size() <= 0)
return a;
for(int i = 0; i < a.size(); i++)
{
BooleanGrid abg = a.get(i);
boolean aMatched = false;
for(int j = 0; j < b.size(); j++)
{
if(abg.attribute().getExtruder().getID() == b.attribute(j).getExtruder().getID())
{
result.add(BooleanGrid.difference(abg, b.get(j)));
aMatched = true;
break;
}
}
if(!aMatched)
result.add(abg);
}
return result.unionDuplicates();
}
}