All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gov.nasa.worldwind.util.combine.ShapeCombiner Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2014 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */
package gov.nasa.worldwind.util.combine;

import gov.nasa.worldwind.geom.Sector;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.util.*;

import com.jogamp.opengl.glu.*;
import java.util.*;

/**
 * @author dcollins
 * @version $Id: ShapeCombiner.java 2413 2014-10-30 21:33:37Z dcollins $
 */
public class ShapeCombiner
{
    protected Globe globe;
    protected double resolution;

    public ShapeCombiner(Globe globe, double resolution)
    {
        if (globe == null)
        {
            String msg = Logging.getMessage("nullValue.GlobeIsNull");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }

        this.globe = globe;
        this.resolution = resolution;
    }

    public Globe getGlobe()
    {
        return this.globe;
    }

    public double getResolution()
    {
        return this.resolution;
    }

    public ContourList union(Combinable... shapes)
    {
        if (shapes == null)
        {
            String msg = Logging.getMessage("nullValue.ListIsNull");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }

        CombineContext cc = this.createContext();

        try
        {
            this.union(cc, shapes);
        }
        finally
        {
            cc.dispose(); // releases GLU tessellator resources
        }

        return cc.getContours();
    }

    public ContourList intersection(Combinable... shapes)
    {
        if (shapes == null)
        {
            String msg = Logging.getMessage("nullValue.ListIsNull");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }

        CombineContext cc = this.createContext();

        try
        {
            if (shapes.length == 1)
                this.union(cc, shapes); // equivalent to the identity of the first shape
            else if (shapes.length > 1)
                this.intersection(cc, shapes);
        }
        finally
        {
            cc.dispose(); // releases GLU tessellator resources
        }

        return cc.getContours();
    }

    public ContourList difference(Combinable... shapes)
    {
        if (shapes == null)
        {
            String msg = Logging.getMessage("nullValue.ListIsNull");
            Logging.logger().severe(msg);
            throw new IllegalArgumentException(msg);
        }

        CombineContext cc = this.createContext();
        try
        {
            if (shapes.length == 1)
                this.union(cc, shapes); // equivalent to the identity of the first shape
            else if (shapes.length > 1)
                this.difference(cc, shapes);
        }
        finally
        {
            cc.dispose(); // releases GLU tessellator resources
        }

        return cc.getContours();
    }

    protected CombineContext createContext()
    {
        return new CombineContext(this.globe, this.resolution);
    }

    protected void union(CombineContext cc, Combinable... shapes)
    {
        GLUtessellator tess = cc.getTessellator();

        try
        {
            GLU.gluTessProperty(tess, GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_NONZERO);
            GLU.gluTessBeginPolygon(tess, null);

            for (Combinable combinable : shapes)
            {
                combinable.combine(cc);
            }
        }
        finally
        {
            GLU.gluTessEndPolygon(tess);
        }
    }

    protected void reverseUnion(CombineContext cc, Combinable... shapes)
    {
        GLUtessellator tess = cc.getTessellator();

        try
        {
            GLU.gluTessProperty(tess, GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_NONZERO);
            GLU.gluTessNormal(tess, 0, 0, -1); // reverse the winding order of the tessellated boundaries
            GLU.gluTessBeginPolygon(tess, null);

            for (Combinable combinable : shapes)
            {
                combinable.combine(cc);
            }
        }
        finally
        {
            GLU.gluTessEndPolygon(tess);
        }
    }

    protected void intersection(CombineContext cc, Combinable... shapes)
    {
        // Limit this operation to the intersection of the bounding regions. Since this is an intersection operation,
        // shapes outside of this region can be ignored or simplified.
        this.assembleBoundingSectors(cc, shapes);

        // Exit immediately if the bounding regions do not intersect.
        Sector sector = Sector.intersection(cc.getBoundingSectors());
        if (sector == null)
            return;

        cc.setSector(sector);

        // Compute the intersection of the first two shapes.
        this.intersection(cc, shapes[0], shapes[1]);

        // When the caller has specified more than two shapes, repeatedly compute the intersection of the current
        // contours with the next shape. This has the effect of progressively computing the intersection of all shapes.
        if (shapes.length > 2)
        {
            for (int i = 2; i < shapes.length; i++)
            {
                ContourList result = new ContourList(cc.getContours());
                cc.removeAllContours();

                this.intersection(cc, result, shapes[i]);
            }
        }
    }

    protected void intersection(CombineContext cc, Combinable a, Combinable b)
    {
        GLUtessellator tess = cc.getTessellator();

        try
        {
            GLU.gluTessProperty(tess, GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_ABS_GEQ_TWO);
            GLU.gluTessBeginPolygon(tess, null);

            a.combine(cc);
            b.combine(cc);
        }
        finally
        {
            GLU.gluTessEndPolygon(tess);
        }
    }

    protected void difference(CombineContext cc, Combinable... shapes)
    {
        // Limit this operation to the region bounding the shape that we're subtracting from. Since this is a difference
        // operation, shapes outside of this region can be ignored or simplified.
        Combinable a = shapes[0];
        this.assembleBoundingSectors(cc, a);

        // Exit immediately if the first shape has no bounding region.
        if (cc.getBoundingSectors().size() == 0)
            return;

        cc.setSector(cc.getBoundingSectors().get(0));

        // Compute the union of all shapes except the first, but reverse the winding order of the resultant contours.
        this.reverseUnion(cc, Arrays.copyOfRange(shapes, 1, shapes.length));
        Combinable b = new ContourList(cc.getContours());
        cc.removeAllContours(); // clear the context's contour list; we use it to store the final contours below

        // Combine the first shape with the union of all shapes exception the first. Since the union has its winding
        // order reversed, this has the effect of subtracting the union from the first shape.
        GLUtessellator tess = cc.getTessellator();
        try
        {
            GLU.gluTessProperty(tess, GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_POSITIVE);
            GLU.gluTessNormal(tess, 0, 0, 1); // restore the GLU tessellator's normal vector
            GLU.gluTessBeginPolygon(tess, null);

            a.combine(cc);
            b.combine(cc);
        }
        finally
        {
            GLU.gluTessEndPolygon(tess);
        }
    }

    protected void assembleBoundingSectors(CombineContext cc, Combinable... shapes)
    {
        try
        {
            cc.setBoundingSectorMode(true);

            for (Combinable combinable : shapes)
            {
                combinable.combine(cc);
            }
        }
        finally
        {
            cc.setBoundingSectorMode(false);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy