package edu.stanford.rsl.conrad.geometry.shapes.simple; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import edu.stanford.rsl.conrad.geometry.AbstractCurve; import edu.stanford.rsl.conrad.geometry.AbstractSurface; import edu.stanford.rsl.conrad.geometry.Axis; import edu.stanford.rsl.conrad.geometry.General; import edu.stanford.rsl.conrad.geometry.bounds.AbstractBoundingCondition; import edu.stanford.rsl.conrad.geometry.transforms.ComboTransform; import edu.stanford.rsl.conrad.geometry.transforms.Transform; import edu.stanford.rsl.conrad.numerics.SimpleMatrix; import edu.stanford.rsl.conrad.numerics.SimpleVector; import edu.stanford.rsl.conrad.utils.CONRAD; /** * <p> CONRAD Class to model surfaces that can efficiently determine the membership of an arbitrary point. * <p> The main features provided by this class are point and curve membership verification and bounds checking. * * * @author Rotimi X Ojo * */ public abstract class SimpleSurface extends AbstractSurface { private static final long serialVersionUID = 1165067022561672962L; protected Transform transform; protected ArrayList<AbstractBoundingCondition> boundingConditions = new ArrayList<AbstractBoundingCondition>(); public SimpleSurface(){ super(); } public SimpleSurface(SimpleSurface surf){ super(surf); // deep copy the bounding conditions list if (surf.boundingConditions != null){ Iterator<AbstractBoundingCondition> it = surf.boundingConditions.iterator(); boundingConditions = new ArrayList<AbstractBoundingCondition>(); while (it.hasNext()) { AbstractBoundingCondition cond = it.next(); boundingConditions.add((cond!=null) ? cond.clone() : null); } } else { boundingConditions = null; } // copy the underlying transform transform = (surf.transform != null) ? surf.transform.clone() : null; } @Override public ArrayList<PointND> intersect(AbstractCurve other) { StraightLine buff = (StraightLine) other; StraightLine line = new StraightLine(buff.getPoint().clone(), buff.getDirection().clone()); line.applyTransform(transform.inverse()); ArrayList<PointND> hitsOnShape = getHits(line); if(hitsOnShape.size() > 0 && boundingConditions.size() > 0){ hitsOnShape = removeOBHits(hitsOnShape, line); hitsOnShape = addBoundingHits(hitsOnShape, line); } return getCorrectedHits(hitsOnShape); } public abstract ArrayList<PointND> getHits(AbstractCurve other); /** * After having obtained the object hits we need to add the bounding condition hits * @param hitsOnBaseShape * @param other * @return */ protected ArrayList<PointND> addBoundingHits(ArrayList<PointND> hitsOnBaseShape, AbstractCurve other){ Iterator<AbstractBoundingCondition> boundingclips = boundingConditions.iterator(); while (boundingclips.hasNext()) { AbstractBoundingCondition currClip = boundingclips.next(); ArrayList<PointND> hitsarr = null; try { hitsarr = currClip.getBoundingSurface().intersect(other); } catch (Exception e) { continue; } Iterator<PointND> cliphits = hitsarr.iterator(); while (cliphits.hasNext()) { PointND currHit = cliphits.next(); if (isMember(currHit, true)) { hitsOnBaseShape.add(currHit); } } } return hitsOnBaseShape; } /** * Remove out of bounds hits * @param hitsOnBaseShape * @param other * @return the list without out-of-bound hits */ protected ArrayList<PointND> removeOBHits(ArrayList<PointND> hitsOnBaseShape, AbstractCurve other) { Iterator<PointND> baseHits = hitsOnBaseShape.iterator(); while(baseHits.hasNext()){ PointND currHit = baseHits.next(); Iterator<AbstractBoundingCondition> boundingclips = boundingConditions.iterator(); while(boundingclips.hasNext()){ if(!boundingclips.next().isSatisfiedBy(currHit)){ baseHits.remove(); break; } } } return hitsOnBaseShape; } /** * Calculates matrix for rotating shape from a principal axis to new axis. * Special case of change of coordinate. * @param newAxis * @return the matrix */ public SimpleMatrix getChangeOfAxisMatrix(Axis newAxis) { if(newAxis == null || General.areColinear(getPrincipalAxis().getAxisVector(), newAxis.getAxisVector(), CONRAD.FLOAT_EPSILON)){ SimpleMatrix rotator = new SimpleMatrix(getPrincipalAxis().dimension(),getPrincipalAxis().dimension()); rotator.identity(); return rotator; } SimpleVector Z = newAxis.getAxisVector(); SimpleVector Y = new Axis(General.crossProduct(Z, getPrincipalAxis().getAxisVector())).getAxisVector(); SimpleVector X = General.crossProduct(Z, Y); double [][] data = {X.copyAsDoubleArray(),Y.copyAsDoubleArray(),Z.copyAsDoubleArray()}; return new SimpleMatrix(data).transposed(); } /** * Transforms given point (usually from object space to world space). * @param hits hits to be transformed by tranformer. * @return ArrayList of hits in new space */ protected ArrayList<PointND> getCorrectedHits(ArrayList<PointND> hits){ ArrayList<PointND> correctedHits = new ArrayList<PointND>(2); Iterator<PointND> quadHits = hits.iterator(); while (quadHits.hasNext()) { PointND val = transform.transform(quadHits.next()); correctedHits.add(val); } return correctedHits; } /** * Determines if the given point in within the bounds of shape; * @param point The point needs to be non-transformed, hence a world coordinate system point * @return true if the point is within the surface */ public boolean isMember(PointND point){ return isMember(point, false); } /** * Determines if the given point in within the bounds of shape; * @param point * @param pointTransformed if true, the point is already in a transformed state, otherwise it needs to be transformed * @return true if the point is within the surface */ public abstract boolean isMember(PointND point, boolean pointTransformed); /** * Adds a bounding condition to the top of the "stack". * @param condition */ public void addBoundingCondition(AbstractBoundingCondition condition){ boundingConditions.add(0,condition); } public abstract Axis getPrincipalAxis(); /** * Adds a collection of bounding conditions * @param conditions */ public void addAllBoundingConditions(Collection<? extends AbstractBoundingCondition> conditions){ boundingConditions.addAll(conditions); } @Override public void applyTransform(Transform t) { transform = new ComboTransform(transform, t); // transform the eight corners: PointND [] corners = new PointND[8]; corners[0] = new PointND(min.get(0), min.get(1), min.get(2)); corners[1] = new PointND(min.get(0), min.get(1), max.get(2)); corners[2] = new PointND(min.get(0), max.get(1), min.get(2)); corners[3] = new PointND(min.get(0), max.get(1), max.get(2)); corners[4] = new PointND(max.get(0), min.get(1), min.get(2)); corners[5] = new PointND(max.get(0), min.get(1), max.get(2)); corners[6] = new PointND(max.get(0), max.get(1), min.get(2)); corners[7] = new PointND(max.get(0), max.get(1), max.get(2)); min = new PointND(Double.MAX_VALUE,Double.MAX_VALUE,Double.MAX_VALUE); max = new PointND(-Double.MAX_VALUE,-Double.MAX_VALUE,-Double.MAX_VALUE); for (int i=0; i < 8; i++){ PointND transf = t.transform(corners[i]); min.updateIfLower(transf); max.updateIfHigher(transf); } } @Override public int getInternalDimension() { return 0; } @Override public PointND evaluate(PointND u) { return null; } public float[] getRasterPoints(int elementCountU, int elementCountV) { return null; } /** * Returns cloned copy of shape's affine transform * @return the transform */ public Transform getTransform() { return transform.clone(); } } /* * Copyright (C) 2010-2014 Andreas Maier, Rotimi X Ojo * CONRAD is developed as an Open Source project under the GNU General Public License (GPL). */