/*
* Copyright 2015 Brandon Borkholder
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jogamp.glg2d.impl;
import java.awt.BasicStroke;
import java.awt.geom.PathIterator;
import com.jogamp.opengl.GLException;
import com.jogamp.opengl.glu.GLU;
import com.jogamp.opengl.glu.GLUtessellator;
import com.jogamp.opengl.glu.GLUtessellatorCallback;
import com.jogamp.opengl.glu.GLUtessellatorCallbackAdapter;
import org.jogamp.glg2d.VertexBuffer;
/**
* Fills a shape by tesselating it with the GLU library. This is a slower
* implementation and {@code FillNonintersectingPolygonVisitor} should be used
* when possible.
*/
public abstract class AbstractTesselatorVisitor extends SimplePathVisitor {
protected GLUtessellator tesselator;
protected GLUtessellatorCallback callback;
/**
* Last command was a move to. This is where drawing starts.
*/
protected float[] drawStart = new float[2];
protected boolean drawing = false;
protected int drawMode;
protected VertexBuffer vBuffer = new VertexBuffer(1024);
public AbstractTesselatorVisitor() {
callback = new TessellatorCallback();
}
@Override
public void setStroke(BasicStroke stroke) {
// nop
}
@Override
public void beginPoly(int windingRule) {
tesselator = GLU.gluNewTess();
configureTesselator(windingRule);
GLU.gluTessBeginPolygon(tesselator, null);
}
protected void configureTesselator(int windingRule) {
switch (windingRule) {
case PathIterator.WIND_EVEN_ODD:
GLU.gluTessProperty(tesselator, GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_ODD);
break;
case PathIterator.WIND_NON_ZERO:
GLU.gluTessProperty(tesselator, GLU.GLU_TESS_WINDING_RULE, GLU.GLU_TESS_WINDING_NONZERO);
break;
}
GLU.gluTessCallback(tesselator, GLU.GLU_TESS_VERTEX, callback);
GLU.gluTessCallback(tesselator, GLU.GLU_TESS_BEGIN, callback);
GLU.gluTessCallback(tesselator, GLU.GLU_TESS_END, callback);
GLU.gluTessCallback(tesselator, GLU.GLU_TESS_ERROR, callback);
GLU.gluTessCallback(tesselator, GLU.GLU_TESS_COMBINE, callback);
GLU.gluTessNormal(tesselator, 0, 0, -1);
}
@Override
public void moveTo(float[] vertex) {
endIfRequired();
drawStart[0] = vertex[0];
drawStart[1] = vertex[1];
}
@Override
public void lineTo(float[] vertex) {
startIfRequired();
addVertex(vertex);
}
private void addVertex(float[] vertex) {
double[] v = new double[3];
v[0] = vertex[0];
v[1] = vertex[1];
GLU.gluTessVertex(tesselator, v, 0, v);
}
@Override
public void closeLine() {
endIfRequired();
}
@Override
public void endPoly() {
// shapes may just end on the starting point without calling closeLine
endIfRequired();
GLU.gluTessEndPolygon(tesselator);
GLU.gluDeleteTess(tesselator);
}
private void startIfRequired() {
if (!drawing) {
GLU.gluTessBeginContour(tesselator);
addVertex(drawStart);
drawing = true;
}
}
private void endIfRequired() {
if (drawing) {
GLU.gluTessEndContour(tesselator);
drawing = false;
}
}
protected void beginTess(int type) {
drawMode = type;
vBuffer.clear();
}
protected void addTessVertex(double[] vertex) {
vBuffer.addVertex((float) vertex[0], (float) vertex[1]);
}
protected abstract void endTess();
protected class TessellatorCallback extends GLUtessellatorCallbackAdapter {
@Override
public void begin(int type) {
beginTess(type);
}
@Override
public void end() {
endTess();
}
@Override
public void vertex(Object vertexData) {
assert vertexData instanceof double[] : "Invalid assumption";
addTessVertex((double[]) vertexData);
}
@Override
public void combine(double[] coords, Object[] data, float[] weight, Object[] outData) {
outData[0] = coords;
}
@Override
public void error(int errnum) {
throw new GLException("Tesselation Error: " + new GLU().gluErrorString(errnum));
}
}
}