/**
* This file is part of VisiCut.
* Copyright (C) 2011 - 2013 Thomas Oster <thomas.oster@rwth-aachen.de>
* RWTH Aachen University - 52062 Aachen, Germany
*
* VisiCut is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VisiCut is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with VisiCut. If not, see <http://www.gnu.org/licenses/>.
**/
package com.t_oster.visicut.model;
import com.t_oster.liblasercut.LaserJob;
import com.t_oster.liblasercut.LaserProperty;
import com.t_oster.liblasercut.platform.Util;
import com.t_oster.uicomponents.ImageListable;
import com.t_oster.visicut.misc.Helper;
import com.t_oster.visicut.model.graphicelements.GraphicObject;
import com.t_oster.visicut.model.graphicelements.GraphicSet;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.LinkedList;
import java.util.List;
/**
* A cutting Profile represents a specific way of handling Image
* Parts. This means a CuttingProfile provides methods
* to generate preview and laser data out of Graphic parts.
* @author Thomas Oster <thomas.oster@rwth-aachen.de>
*/
public abstract class LaserProfile implements ImageListable, Cloneable
{
private double DPI = 500;
/**
* Get the value of DPI
*
* @return the value of DPI
*/
public double getDPI()
{
return DPI;
}
/**
* Set the value of DPI
*
* @param DPI new value of DPI
*/
public void setDPI(double DPI)
{
this.DPI = DPI;
}
protected String description = "A new Laserprofile";
/**
* Get the value of description
*
* @return the value of description
*/
public String getDescription()
{
return description;
}
/**
* Set the value of description
*
* @param description new value of description
*/
public void setDescription(String description)
{
this.description = description;
}
protected String thumbnailPath = null;
/**
* Get the value of thumbnailPath
*
* @return the value of thumbnailPath
*/
public String getThumbnailPath()
{
return thumbnailPath;
}
/**
* Set the value of thumbnailPath
*
* @param thumbnailPath new value of thumbnailPath
*/
public void setThumbnailPath(String thumbnailPath)
{
this.thumbnailPath = thumbnailPath;
}
protected String name = "Unnamed Profile";
/**
* Get the value of name
*
* @return the value of name
*/
public String getName()
{
return name;
}
/**
* Set the value of name
*
* @param name new value of name
*/
public void setName(String name)
{
this.name = name;
}
//this attribute is unused and only kept for compatibility
//with old XML files
private transient boolean temporaryCopy;
public abstract void renderPreview(Graphics2D g, GraphicSet objects, MaterialProfile material, AffineTransform mm2px);
public abstract void addToLaserJob(LaserJob job, GraphicSet objects, List<LaserProperty> laserProperties);
@Override
public String toString()
{
return (this.getName() != null ? this.getName() : super.toString());
}
@Override
public abstract LaserProfile clone();
/**
* Decomposes a GraphicSet into disjoint paths which
* have a distance bigger than the sum of their lengths
* @param set
* @return
*/
public LinkedList<GraphicSet> decompose(GraphicSet set)
{
LinkedList<GraphicSet> result = new LinkedList<GraphicSet>();
//TODO: Develope a good algorithm. For now we just
//return a set containing a single set
if (true)
{
result.add(set);
return result;
}
for (GraphicObject o:set)
{
//We assign every Object to a result bin
//first we get the dimension of the object
Rectangle2D bb = o.getBoundingBox();
if (set.getTransform() != null)
{//and transform it accordingly
bb = Helper.transform(bb, set.getTransform());
}
findBin:
{//now we see if we have a bin which is near enough
for (GraphicSet s:result)
{
Rectangle2D sbb = s.getBoundingBox();
if (Helper.distance(bb, sbb) < Math.min(bb.getWidth()+sbb.getWidth(), bb.getHeight()+sbb.getHeight()))
{
s.add(o);
break findBin;
}
}
//no bin found => create a new one
GraphicSet s = new GraphicSet();
s.setBasicTransform(set.getBasicTransform());
s.setTransform(set.getTransform());
s.add(o);
}
}
//Now merge the bins until no one is near enough
//TODO: Efficient merging?
return result;
}
@Override
abstract public int hashCode();
/**
* create a hashCode() of all properties of the base class LaserProfile
* use this in hashCode() methods for subclasses
* @return integer
*/
public int hashCodeBase() {
int hash = (int)DPI*31;
if (description != null) {
hash += description.hashCode() * 3;
}
if (thumbnailPath != null) {
hash += thumbnailPath.hashCode() * 7;
}
return hash;
}
@Override
abstract public boolean equals(Object obj);
/**
* checks for equality of all properties of the base class LaserProfile
* use this in equals() methods for subclasses
* @param obj
* @return true when all general LaserProfile properties are equal
*/
protected boolean equalsBase(Object obj)
{
if (obj == null)
{
return false;
}
if (!getClass().equals(obj.getClass()))
{
return false;
}
final LaserProfile other = (LaserProfile) obj;
if (Util.differ(this.name, other.name))
{
return false;
}
if (Double.doubleToLongBits(this.DPI) != Double.doubleToLongBits(other.DPI))
{
return false;
}
if (Util.differ(this.description, other.description))
{
return false;
}
if (Util.differ(this.thumbnailPath,other.thumbnailPath))
{
return false;
}
return true;
}
}