/* * Copyright 2014 The Apache Software Foundation. * * 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.apache.pdfbox.pdmodel.graphics.shading; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.List; /** * Patch is extended by CoonsPatch and TensorPatch. This was done as part of * GSoC2014, Tilman Hausherr is the mentor. * * @author Shaola Ren */ abstract class Patch { protected Point2D[][] controlPoints; protected float[][] cornerColor; /* level = {levelU, levelV}, levelU defines the patch's u direction edges should be divided into 2^levelU parts, level V defines the patch's v direction edges should be divided into 2^levelV parts */ protected int[] level; protected List<ShadedTriangle> listOfTriangles; /** * Constructor of Patch. * * @param ctl control points, size is 12 (for type 6 shading) or 16 (for * type 7 shading) * @param color 4 corner's colors */ Patch(Point2D[] ctl, float[][] color) { cornerColor = color.clone(); } /** * Get the implicit edge for flag = 1. * * @return implicit control points */ protected abstract Point2D[] getFlag1Edge(); /** * Get the implicit edge for flag = 2. * * @return implicit control points */ protected abstract Point2D[] getFlag2Edge(); /** * Get the implicit edge for flag = 3. * * @return implicit control points */ protected abstract Point2D[] getFlag3Edge(); /** * Get the implicit color for flag = 1. * * @return color */ protected float[][] getFlag1Color() { int numberOfColorComponents = cornerColor[0].length; float[][] implicitCornerColor = new float[2][numberOfColorComponents]; for (int i = 0; i < numberOfColorComponents; i++) { implicitCornerColor[0][i] = cornerColor[1][i]; implicitCornerColor[1][i] = cornerColor[2][i]; } return implicitCornerColor; } /** * Get implicit color for flag = 2. * * @return color */ protected float[][] getFlag2Color() { int numberOfColorComponents = cornerColor[0].length; float[][] implicitCornerColor = new float[2][numberOfColorComponents]; for (int i = 0; i < numberOfColorComponents; i++) { implicitCornerColor[0][i] = cornerColor[2][i]; implicitCornerColor[1][i] = cornerColor[3][i]; } return implicitCornerColor; } /** * Get implicit color for flag = 3. * * @return color */ protected float[][] getFlag3Color() { int numberOfColorComponents = cornerColor[0].length; float[][] implicitCornerColor = new float[2][numberOfColorComponents]; for (int i = 0; i < numberOfColorComponents; i++) { implicitCornerColor[0][i] = cornerColor[3][i]; implicitCornerColor[1][i] = cornerColor[0][i]; } return implicitCornerColor; } /** * Calculate the distance from point ps to point pe. * * @param ps one end of a line * @param pe the other end of the line * @return length of the line */ protected double getLen(Point2D ps, Point2D pe) { double x = pe.getX() - ps.getX(); double y = pe.getY() - ps.getY(); return Math.sqrt(x * x + y * y); } /** * Whether the for control points are on a line. * * @param ctl an edge's control points, the size of ctl is 4 * @return true when 4 control points are on a line, otherwise false */ protected boolean isEdgeALine(Point2D[] ctl) { double ctl1 = Math.abs(edgeEquationValue(ctl[1], ctl[0], ctl[3])); double ctl2 = Math.abs(edgeEquationValue(ctl[2], ctl[0], ctl[3])); double x = Math.abs(ctl[0].getX() - ctl[3].getX()); double y = Math.abs(ctl[0].getY() - ctl[3].getY()); return (ctl1 <= x && ctl2 <= x) || (ctl1 <= y && ctl2 <= y); } /** * A line from point p1 to point p2 defines an equation, adjust the form of * the equation to let the rhs equals 0, then calculate the lhs value by * plugging the coordinate of p in the lhs expression. * * @param p target point * @param p1 one end of a line * @param p2 the other end of a line * @return calculated value */ protected double edgeEquationValue(Point2D p, Point2D p1, Point2D p2) { return (p2.getY() - p1.getY()) * (p.getX() - p1.getX()) - (p2.getX() - p1.getX()) * (p.getY() - p1.getY()); } /** * An assistant method to accomplish type 6 and type 7 shading. * * @param patchCC all the crossing point coordinates and color of a grid * @return a ShadedTriangle list which can compose the grid patch */ protected List<ShadedTriangle> getShadedTriangles(CoordinateColorPair[][] patchCC) { List<ShadedTriangle> list = new ArrayList<>(); int szV = patchCC.length; int szU = patchCC[0].length; for (int i = 1; i < szV; i++) { for (int j = 1; j < szU; j++) { Point2D p0 = patchCC[i - 1][j - 1].coordinate, p1 = patchCC[i - 1][j].coordinate, p2 = patchCC[i][j].coordinate, p3 = patchCC[i][j - 1].coordinate; boolean ll = true; if (overlaps(p0, p1) || overlaps(p0, p3)) { ll = false; } else { // p0, p1 and p3 are in counter clock wise order, p1 has priority over p0, p3 has priority over p1 Point2D[] llCorner = { p0, p1, p3 }; float[][] llColor = { patchCC[i - 1][j - 1].color, patchCC[i - 1][j].color, patchCC[i][j - 1].color }; ShadedTriangle tmpll = new ShadedTriangle(llCorner, llColor); // lower left triangle list.add(tmpll); } if (ll && (overlaps(p2, p1) || overlaps(p2, p3))) { } else { // p3, p1 and p2 are in counter clock wise order, p1 has priority over p3, p2 has priority over p1 Point2D[] urCorner = { p3, p1, p2 }; float[][] urColor = { patchCC[i][j - 1].color, patchCC[i - 1][j].color, patchCC[i][j].color }; ShadedTriangle tmpur = new ShadedTriangle(urCorner, urColor); // upper right triangle list.add(tmpur); } } } return list; } // whether two points p0 and p1 are degenerated into one point within the coordinates' accuracy 0.001 private boolean overlaps(Point2D p0, Point2D p1) { return Math.abs(p0.getX() - p1.getX()) < 0.001 && Math.abs(p0.getY() - p1.getY()) < 0.001; } }