001/*
002 *   ControlPointNet  -- A network or matrix of control points for a NURBS surface in nD space.
003 *
004 *   Copyright (C) 2010-2015, Joseph A. Huwaldt
005 *   All rights reserved.
006 *
007 *   This library is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *
012 *   This library is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 *   Lesser General Public License for more details.
016 *
017 *   You should have received a copy of the GNU Lesser General Public License
018 *   along with this program; if not, write to the Free Software
019 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
020 *   Or visit:  http://www.gnu.org/licenses/lgpl.html
021 */
022package geomss.geom.nurbs;
023
024import geomss.geom.Point;
025import java.text.MessageFormat;
026import java.util.Iterator;
027import java.util.List;
028import static java.util.Objects.requireNonNull;
029import java.util.ResourceBundle;
030import javax.measure.converter.ConversionException;
031import javax.measure.quantity.Length;
032import javax.measure.unit.Unit;
033import javolution.context.ObjectFactory;
034import javolution.context.StackContext;
035import javolution.lang.ValueType;
036import javolution.text.Text;
037import javolution.text.TextBuilder;
038import javolution.util.FastTable;
039import javolution.xml.XMLFormat;
040import javolution.xml.XMLSerializable;
041import javolution.xml.stream.XMLStreamException;
042
043/**
044 * A network or matrix of control points for a NURBS surface in n-dimensional space.
045 *
046 * <p> Modified by: Joseph A. Huwaldt </p>
047 *
048 * @author Joseph A. Huwaldt, Date: June 15, 2010
049 * @version November 28, 2015
050 */
051@SuppressWarnings("serial")
052public class ControlPointNet implements Iterable<List<ControlPoint>>, Cloneable, XMLSerializable, ValueType {
053
054    /**
055     * The resource bundle for this package.
056     */
057    private static final ResourceBundle RESOURCES = geomss.geom.AbstractGeomElement.RESOURCES;
058
059    // A list of lists of ControlPoint objects.
060    private FastTable<FastTable<ControlPoint>> _matrix;
061
062    /**
063     * Returns a {@link ControlPointNet} instance made up of the control points in the
064     * specified java matrix.
065     *
066     * @param cps Matrix of control points: cps[t][s]. s-parameter runs down a column of
067     *            points and the t-parameter runs across the columns of points. May not be
068     *            null.
069     * @return A ControlPointNet object using the data in the specified matrix.
070     */
071    public static ControlPointNet valueOf(ControlPoint[][] cps) {
072        requireNonNull(cps, MessageFormat.format(RESOURCES.getString("paramNullErr"), "cps"));
073        int nt = cps.length;
074        if (nt < 1)
075            throw new IllegalArgumentException(
076                    MessageFormat.format(RESOURCES.getString("zeroLengthListErr"), "cps"));
077        int ns = cps[0].length;
078        if (ns < 1)
079            throw new IllegalArgumentException(
080                    MessageFormat.format(RESOURCES.getString("zeroLengthListErr"), "cps[0]"));
081        for (int t = 1; t < nt; ++t) {
082            if (cps[t].length != ns)
083                throw new IllegalArgumentException(
084                        MessageFormat.format(RESOURCES.getString("irregMatrixErr"), "cps"));
085        }
086
087        Unit<Length> refUnit = cps[0][0].getUnit();
088        ControlPointNet P = FACTORY.object();
089        P._matrix = FastTable.newInstance();
090        for (int t = 0; t < nt; ++t) {
091            FastTable<ControlPoint> tbl = FastTable.newInstance();
092            for (int s = 0; s < ns; ++s) {
093                tbl.add(cps[t][s].to(refUnit));
094            }
095            P._matrix.add(tbl);
096        }
097
098        return P;
099    }
100
101    /**
102     * Returns a {@link ControlPointNet} instance made up of the control points in the
103     * specified list of lists.
104     *
105     * @param cps List of lists (matrix) of control points: cps.get(t).get(s). s-parameter
106     *            runs down a column of points and the t-parameter runs across the columns
107     *            of points. May not be null.
108     * @return A ControlPointNet object using the data in the specified matrix.
109     */
110    public static ControlPointNet valueOf(List<? extends List<ControlPoint>> cps) {
111        requireNonNull(cps, MessageFormat.format(RESOURCES.getString("paramNullErr"), "cps"));
112        int nt = cps.size();
113        if (nt < 1)
114            throw new IllegalArgumentException(
115                    MessageFormat.format(RESOURCES.getString("zeroLengthListErr"), "cps"));
116        int ns = cps.get(0).size();
117        if (ns < 1)
118            throw new IllegalArgumentException(
119                    MessageFormat.format(RESOURCES.getString("zeroLengthListErr"), "cps.get(0)"));
120        for (int t = 1; t < nt; ++t) {
121            if (cps.get(t).size() != ns)
122                throw new IllegalArgumentException(
123                        MessageFormat.format(RESOURCES.getString("irregMatrixErr"), "cps"));
124        }
125
126        Unit<Length> refUnit = cps.get(0).get(0).getUnit();
127        ControlPointNet P = FACTORY.object();
128        P._matrix = FastTable.newInstance();
129        for (int t = 0; t < nt; ++t) {
130            FastTable<ControlPoint> tbl = FastTable.newInstance();
131            int size = cps.get(t).size();
132            for (int i = 0; i < size; ++i) {
133                ControlPoint cp = cps.get(t).get(i);
134                tbl.add(cp.to(refUnit));
135            }
136            P._matrix.add(tbl);
137        }
138
139        return P;
140    }
141
142    /**
143     * Returns a {@link ControlPointNet} instance made up of the control points contained
144     * in the specified network.
145     *
146     * @param cpNet An existing control point net. The control points from this network
147     *              are used to make this one. May not be null.
148     * @return A ControlPointNet object using the data in the specified network.
149     */
150    public static ControlPointNet valueOf(ControlPointNet cpNet) {
151        requireNonNull(cpNet, MessageFormat.format(RESOURCES.getString("paramNullErr"), "cpNet"));
152        ControlPointNet P = FACTORY.object();
153        P._matrix = FastTable.newInstance();
154        int ncols = cpNet.getNumberOfColumns();
155        for (int i = 0; i < ncols; ++i) {
156            List<ControlPoint> column = cpNet.getColumn(i);
157            FastTable<ControlPoint> tbl = FastTable.newInstance();
158            tbl.addAll(column);
159            P._matrix.add(tbl);
160        }
161        return P;
162    }
163
164    /**
165     * Returns the total number of control points in this matrix of control points.
166     *
167     * @return The total number of control points in this network.
168     */
169    public int size() {
170        return _matrix.size() * _matrix.get(0).size();
171    }
172
173    /**
174     * Return the control point matrix size in the s-direction (down a column of control
175     * points).
176     *
177     * @return The number of rows in this control point network.
178     */
179    public int getNumberOfRows() {
180        return _matrix.get(0).size();
181    }
182
183    /**
184     * Return the control point matrix size in the t-direction (across the columns of
185     * control points).
186     *
187     * @return The number of columns in this control point network.
188     */
189    public int getNumberOfColumns() {
190        return _matrix.size();
191    }
192
193    /**
194     * Returns the ControlPoint at the specified s,t position in this matrix.
195     *
196     * @param s the index in the s-direction (down a column of points).
197     * @param t the index in the t-direction (across the columns of points).
198     * @return the control point at the s,t position in this matrix.
199     * @throws IndexOutOfBoundsException <code>(s &lt; 0) || (s &ge;
200     * getNumberOfRows()) || t &lt; 0 || (t &ge; getNumberOfColumns()) )</code>
201     */
202    public ControlPoint get(int s, int t) {
203        return _matrix.get(t).get(s);
204    }
205
206    /**
207     * Returns a list of ControlPoint objects that represent a single row in this network
208     * of control points.
209     *
210     * @param sIndex The index for the row of control points to return.
211     * @return The specified row of control points.
212     * @throws IndexOutOfBoundsException <code>sIndex &lt; 0 || (sIndex &ge;
213     * getNumberOfRows()) )</code>
214     */
215    public List<ControlPoint> getRow(int sIndex) {
216        FastTable<ControlPoint> list = FastTable.newInstance();
217        int ncol = _matrix.size();
218        for (int i = 0; i < ncol; ++i) {
219            List<ControlPoint> column = _matrix.get(i);
220            list.add(column.get(sIndex));
221        }
222        return list;
223    }
224
225    /**
226     * Returns a list of ControlPoint objects that represent a single column in this
227     * network of control points.
228     *
229     * @param tIndex The index for the column of control points to return.
230     * @return The specified column of control points.
231     * @throws IndexOutOfBoundsException <code>tIndex &lt; 0 || (tIndex &ge;
232     * getNumberOfColumns()) )</code>
233     */
234    public List<ControlPoint> getColumn(int tIndex) {
235        FastTable<ControlPoint> list = FastTable.newInstance();
236        list.addAll(_matrix.get(tIndex));
237        return list;
238    }
239
240    /**
241     * Return the coordinate point representing the minimum bounding box corner (e.g.: min
242     * X, min Y, min Z) of this matrix of ControlPoint objects.
243     *
244     * @return The minimum bounding box coordinate for this geometry element.
245     */
246    public Point getBoundsMin() {
247        StackContext.enter();
248        try {
249            Point minPoint = get(0, 0).getBoundsMin();
250
251            int ncol = _matrix.size();
252            for (int i = 0; i < ncol; ++i) {
253                List<ControlPoint> tbl = _matrix.get(i);
254                int nrow = tbl.size();
255                for (int j = 0; j < nrow; ++j) {
256                    ControlPoint cp = tbl.get(j);
257                    minPoint = minPoint.min(cp.getBoundsMin());
258                }
259            }
260            return StackContext.outerCopy(minPoint);
261
262        } finally {
263            StackContext.exit();
264        }
265    }
266
267    /**
268     * Return the coordinate point representing the maximum bounding box corner (e.g.: max
269     * X, max Y, max Z) of this matrix of ControlPoint objects.
270     *
271     * @return The maximum bounding box coordinate for this geometry element.
272     */
273    public Point getBoundsMax() {
274        StackContext.enter();
275        try {
276            Point maxPoint = get(0, 0).getBoundsMax();
277
278            int ncol = _matrix.size();
279            for (int i = 0; i < ncol; ++i) {
280                List<ControlPoint> tbl = _matrix.get(i);
281                int nrow = tbl.size();
282                for (int j = 0; j < nrow; ++j) {
283                    ControlPoint cp = tbl.get(j);
284                    maxPoint = maxPoint.max(cp.getBoundsMax());
285                }
286            }
287            return StackContext.outerCopy(maxPoint);
288
289        } finally {
290            StackContext.exit();
291        }
292    }
293
294    /**
295     * Return a new control point network that is the transpose of this network (the rows
296     * & columns are swapped).
297     *
298     * @return A new ControlPointNet identical to this one but with the rows and columns
299     *         transposed.
300     */
301    public ControlPointNet transpose() {
302        //StackContext.enter();
303        try {
304            FastTable<FastTable<ControlPoint>> nMat = FastTable.newInstance();
305            int numCols = getNumberOfColumns();
306            int numRows = getNumberOfRows();
307            for (int i = 0; i < numRows; ++i) {
308                FastTable<ControlPoint> col = FastTable.newInstance();
309                nMat.add(col);
310                for (int j = 0; j < numCols; ++j) {
311                    col.add(get(i, j));
312                }
313            }
314
315            ControlPointNet nCpNet = ControlPointNet.valueOf(nMat);
316            return nCpNet;  //StackContext.outerCopy(nCpNet);
317
318        } finally {
319            //StackContext.exit();
320        }
321    }
322
323    /**
324     * Return a new control point network that is identical to this one but with the rows
325     * in reverse order.
326     *
327     * @return A new ControlPointNet identical to this one but with the row order
328     *         reversed.
329     */
330    public ControlPointNet reverseRows() {
331        StackContext.enter();
332        try {
333            FastTable<FastTable<ControlPoint>> nMat = FastTable.newInstance();
334            int numCols = getNumberOfColumns();
335            int numRows = getNumberOfRows();
336            for (int i = 0; i < numCols; ++i) {
337                FastTable<ControlPoint> col = FastTable.newInstance();
338                nMat.add(col);
339                for (int j = numRows - 1; j >= 0; --j) {
340                    col.add(_matrix.get(i).get(j));
341                }
342            }
343
344            ControlPointNet nCpNet = ControlPointNet.valueOf(nMat);
345            return StackContext.outerCopy(nCpNet);
346
347        } finally {
348            StackContext.exit();
349        }
350    }
351
352    /**
353     * Return a new control point network that is identical to this one but with the
354     * columns in reverse order.
355     *
356     * @return A new ControlPointNet identical to this one but with the column order
357     *         reversed.
358     */
359    public ControlPointNet reverseColumns() {
360        StackContext.enter();
361        try {
362            FastTable<List<ControlPoint>> nMat = FastTable.newInstance();
363            int numCols = getNumberOfColumns();
364            for (int i = numCols - 1; i >= 0; --i) {
365                nMat.add(_matrix.get(i));
366            }
367
368            ControlPointNet nCpNet = ControlPointNet.valueOf(nMat);
369            return StackContext.outerCopy(nCpNet);
370
371        } finally {
372            StackContext.exit();
373        }
374    }
375
376    /**
377     * Returns the unit in which the control points in this network are stated.
378     *
379     * @return The unit in which this control point network is stated.
380     */
381    public Unit<Length> getUnit() {
382        return get(0, 0).getUnit();
383    }
384
385    /**
386     * Returns the equivalent to this control point network but stated in the specified
387     * unit.
388     *
389     * @param unit The length unit of the control point to be returned. May not be null.
390     * @return An equivalent to this control point network but stated in the specified
391     *         unit.
392     * @throws ConversionException if the the input unit is not a length unit.
393     */
394    public ControlPointNet to(Unit<Length> unit) throws ConversionException {
395        requireNonNull(unit);
396
397        StackContext.enter();
398        try {
399            FastTable<FastTable<ControlPoint>> nMat = FastTable.newInstance();
400            int ncol = _matrix.size();
401            for (int i = 0; i < ncol; ++i) {
402                List<ControlPoint> lst = _matrix.get(i);
403                FastTable<ControlPoint> nLst = FastTable.newInstance();
404                int nrow = lst.size();
405                for (int j = 0; j < nrow; ++j) {
406                    ControlPoint cp = lst.get(j);
407                    nLst.add(cp.to(unit));
408                }
409                nMat.add(nLst);
410            }
411
412            ControlPointNet nCpNet = ControlPointNet.valueOf(nMat);
413            return StackContext.outerCopy(nCpNet);
414
415        } finally {
416            StackContext.exit();
417        }
418    }
419
420    /**
421     * Return the equivalent of this control point network converted to the specified
422     * number of physical dimensions. If the number of dimensions is greater than this
423     * element, then zeros are added to the additional dimensions. If the number of
424     * dimensions is less than this element, then the extra dimensions are simply dropped
425     * (truncated). If the new dimensions are the same as the dimension of this element,
426     * then this element is simply returned.
427     *
428     * @param newDim The dimension of the surface to return.
429     * @return The equivalent of this control point network converted to the new
430     *         dimensions.
431     */
432    public ControlPointNet toDimension(int newDim) {
433
434        StackContext.enter();
435        try {
436            FastTable<FastTable<ControlPoint>> nMat = FastTable.newInstance();
437            int ncol = _matrix.size();
438            for (int i = 0; i < ncol; ++i) {
439                List<ControlPoint> lst = _matrix.get(i);
440                FastTable<ControlPoint> nLst = FastTable.newInstance();
441                int nrow = lst.size();
442                for (int j = 0; j < nrow; ++j) {
443                    ControlPoint cp = lst.get(j);
444                    ControlPoint nCP = ControlPoint.valueOf(cp.getPoint().toDimension(newDim), cp.getWeight());
445                    nLst.add(nCP);
446                }
447                nMat.add(nLst);
448            }
449
450            ControlPointNet nCpNet = ControlPointNet.valueOf(nMat);
451            return StackContext.outerCopy(nCpNet);
452
453        } finally {
454            StackContext.exit();
455        }
456    }
457
458    /**
459     * Returns an iterator over the lists of ControlPoint objects in this network. The
460     * iterator returns lists containing the columns of control points.
461     */
462    @Override
463    public Iterator<List<ControlPoint>> iterator() {
464        return (Iterator)_matrix.iterator();
465    }
466
467    /**
468     * Return <code>true</code> if this ControlPointNet contains valid and finite
469     * numerical components. A value of <code>false</code> will be returned if any of the
470     * control point values are NaN or Inf.
471     *
472     * @return true if this ControlPointNet contains valid and finite numerical
473     *         components.
474     */
475    public boolean isValid() {
476        int ncol = _matrix.size();
477        for (int i = 0; i < ncol; ++i) {
478            List<ControlPoint> row = _matrix.get(i);
479            int nrow = row.size();
480            for (int j = 0; j < nrow; ++j) {
481                ControlPoint cp = row.get(j);
482                if (!cp.isValid())
483                    return false;
484            }
485        }
486        return true;
487    }
488
489    /**
490     * Returns a copy of this ControlPointNet instance
491     * {@link javolution.context.AllocatorContext allocated} by the calling thread
492     * (possibly on the stack).
493     *
494     * @return an identical and independent copy of this point.
495     */
496    @Override
497    public ControlPointNet copy() {
498        return copyOf(this);
499    }
500
501    /**
502     * Returns a copy of this ControlPointNet instance
503     * {@link javolution.context.AllocatorContext allocated} by the calling thread
504     * (possibly on the stack).
505     *
506     * @return an identical and independent copy of this point.
507     * @throws java.lang.CloneNotSupportedException Never thrown.
508     */
509    @Override
510    @SuppressWarnings("CloneDoesntCallSuperClone")
511    public Object clone() throws CloneNotSupportedException {
512        return copy();
513    }
514
515    /**
516     * Compares this ControlPointNet against the specified object for strict equality
517     * (same sized lists of the equal ControlPoints).
518     *
519     * @param obj the object to compare with.
520     * @return <code>true</code> if this point is identical to that point;
521     *         <code>false</code> otherwise.
522     */
523    @Override
524    public boolean equals(Object obj) {
525        if (this == obj)
526            return true;
527        if ((obj == null) || (obj.getClass() != this.getClass()))
528            return false;
529
530        ControlPointNet that = (ControlPointNet)obj;
531        return this._matrix.equals(that._matrix);
532    }
533
534    /**
535     * Returns the hash code for this parameter.
536     *
537     * @return the hash code value.
538     */
539    @Override
540    public int hashCode() {
541        int hash = 7;
542
543        int var_code = _matrix.hashCode();
544        hash = hash * 31 + var_code;
545
546        return hash;
547    }
548
549    /**
550     * Returns the text representation of this control point matrix that consists of the
551     * the control points listed out.
552     *
553     * @return the text representation of this geometry element.
554     */
555    public Text toText() {
556        TextBuilder tmp = TextBuilder.newInstance();
557        tmp.append('{');
558        tmp.append(_matrix.toText());
559        tmp.append('}');
560        Text txt = tmp.toText();
561        TextBuilder.recycle(tmp);
562        return txt;
563    }
564
565    /**
566     * Returns the String representation of this control point matrix that consists of the
567     * control points listed out.
568     *
569     * @return the text representation of this geometry element.
570     */
571    @Override
572    public String toString() {
573        return toText().toString();
574    }
575
576    /**
577     * Holds the default XML representation for this object.
578     */
579    protected static final XMLFormat<ControlPointNet> XML = new XMLFormat<ControlPointNet>(ControlPointNet.class) {
580        @Override
581        public ControlPointNet newInstance(Class<ControlPointNet> cls, InputElement xml) throws XMLStreamException {
582            ControlPointNet cpNet = FACTORY.object();
583            cpNet._matrix = FastTable.newInstance();
584            return cpNet;
585        }
586
587        @Override
588        public void read(InputElement xml, ControlPointNet obj) throws XMLStreamException {
589
590            FastTable<FastTable<ControlPoint>> matrix = FastTable.newInstance();
591            while (xml.hasNext()) {
592                matrix.add(xml.get("Column", FastTable.class));
593            }
594            obj._matrix.addAll(matrix);
595
596        }
597
598        @Override
599        public void write(ControlPointNet obj, OutputElement xml) throws XMLStreamException {
600
601            for (FastTable<ControlPoint> column : obj._matrix) {
602                xml.add(column, "Column", FastTable.class);
603            }
604
605        }
606    };
607
608    ///////////////////////
609    // Factory creation. //
610    ///////////////////////
611    protected ControlPointNet() {
612    }
613
614    @SuppressWarnings("unchecked")
615    private static final ObjectFactory<ControlPointNet> FACTORY = new ObjectFactory<ControlPointNet>() {
616        @Override
617        protected ControlPointNet create() {
618            return new ControlPointNet();
619        }
620
621        @Override
622        protected void cleanup(ControlPointNet obj) {
623            int ncol = obj._matrix.size();
624            for (int i = 0; i < ncol; ++i) {
625                FastTable<ControlPoint> col = obj._matrix.get(i);
626                col.reset();
627            }
628            obj._matrix.reset();
629            obj._matrix = null;
630        }
631    };
632
633    @SuppressWarnings("unchecked")
634    private static ControlPointNet copyOf(ControlPointNet original) {
635        ControlPointNet o = FACTORY.object();
636        o._matrix = FastTable.newInstance();
637        int ncol = original._matrix.size();
638        for (int i = 0; i < ncol; ++i) {
639            List<ControlPoint> tbl = original._matrix.get(i);
640            FastTable<ControlPoint> tblCopy = FastTable.newInstance();
641            int nrow = tbl.size();
642            for (int j = 0; j < nrow; ++j) {
643                ControlPoint cp = tbl.get(j);
644                tblCopy.add(cp.copy());
645            }
646            o._matrix.add(tblCopy);
647        }
648        return o;
649    }
650
651}