001/******************************************************************************* 002 * This software is provided as a supplement to the authors' textbooks on digital 003 * image processing published by Springer-Verlag in various languages and editions. 004 * Permission to use and distribute this software is granted under the BSD 2-Clause 005 * "Simplified" License (see http://opensource.org/licenses/BSD-2-Clause). 006 * Copyright (c) 2006-2023 Wilhelm Burger, Mark J. Burge. All rights reserved. 007 * Visit https://imagingbook.com for additional details. 008 ******************************************************************************/ 009package imagingbook.common.geometry.shape; 010 011import imagingbook.common.geometry.basic.Pnt2d; 012import imagingbook.common.geometry.ellipse.GeometricEllipse; 013 014import java.awt.Shape; 015import java.awt.geom.PathIterator; 016import java.util.ArrayList; 017import java.util.List; 018 019/** 020 * Implementing classes know how to create an AWT {@link Shape}. 021 * 022 * @author WB 023 * 024 */ 025public interface ShapeProducer { 026 027 /** 028 * Returns a scaled {@link Shape} for this object (default scale is 1). Must be defined by implementing classes. The 029 * interpretation of the scale factor is left to the implementing class. For example, for {@link Pnt2d} it specifies 030 * the size of the marker (see {@link Pnt2d#getShape(double)}. 031 * 032 * @param scale the scale of the shape 033 * @return a {@link Shape} instance 034 */ 035 public Shape getShape(double scale); 036 037 /** 038 * Returns a {@link Shape} for this object at the default scale (1). 039 * 040 * @return a {@link Shape} instance 041 */ 042 public default Shape getShape() { 043 return getShape(1); 044 }; 045 046 /** 047 * Returns a fixed sequence of {@link Shape} items for drawing this object, which must contain at least one item. 048 * This is to produce graphic representations that are too complex for a single {@link Shape} item. The returned 049 * shapes may also be displayed with different strokes or colors. 050 * <p> 051 * By default, this method returns a single item which is the primary shape (obtained by {@link #getShape(double)}). 052 * Implementing classes should override this method if more than one shape must be returned For example, a 053 * {@link GeometricEllipse} returns three shape items: (a) the ellipse curve, (b) the center mark, (c) the major 054 * axes (see {@link GeometricEllipse#getShapes(double)}). 055 * </p> 056 * 057 * @param scale a scale factor (may be used or ignored) 058 * @return sequence of {@link Shape} items 059 */ 060 public default Shape[] getShapes(double scale) { 061 return new Shape[] { getShape(scale) }; 062 } 063 064 public default Shape[] getShapes() { 065 return getShapes(1); 066 } 067 068 // TODO: experimental 069 public static Pnt2d[] getShapePoints(Shape shape) { 070 double[] coords = new double[6]; 071 PathIterator pathIterator = shape.getPathIterator(null); 072 073 List<Pnt2d> points = new ArrayList<>(); 074 075 while (!pathIterator.isDone()) { 076 int segmentType = pathIterator.currentSegment(coords); 077 switch (segmentType) { 078 079 case PathIterator.SEG_MOVETO : 080 System.out.printf("move to x1=%f, y1=%f\n", coords[0], coords[1]); 081 points.add(Pnt2d.from(coords[0], coords[1])); 082 break; 083 084 case PathIterator.SEG_LINETO : 085 System.out.printf("line to x1=%f, y1=%f\n", coords[0], coords[1]); 086 points.add(Pnt2d.from(coords[0], coords[1])); 087 break; 088 089 case PathIterator.SEG_QUADTO : 090 System.out.printf("quad to x1=%f, y1=%f, x2=%f, y2=%f\n", coords[0], coords[1], coords[2], coords[3]); 091 points.add(Pnt2d.from(coords[0], coords[1])); 092 break; 093 094 case PathIterator.SEG_CUBICTO : 095 System.out.printf("cubic to x1=%f, y1=%f, x2=%f, y2=%f, x3=%f, y3=%f\n", 096 coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); 097 points.add(Pnt2d.from(coords[0], coords[1])); 098 break; 099 100 case PathIterator.SEG_CLOSE : 101 System.out.printf("close\n"); 102 break; 103 104 default : throw new RuntimeException("unknown path segment type " + segmentType); 105 } 106 107 108 pathIterator.next(); 109 } 110 return points.toArray(new Pnt2d[points.size()]); 111 } 112 113 114}