public class CubicSubdivision extends DisplayPanel { /** * The paintable sequence variable that will represent * the cubic subdivision structure. * * This sequence will be constructed just before it is * time to paint the cubic subdivision. * * This sequence will be placed in the tile. */ PaintableSequence sequence = null; /** The tile size. */ int size = 500; /** The tile bounds. */ XRect bounds = new XRect(0, 0, size, size); /** The tile to display the cubic structure. */ Tile tile = new Tile(); // The tile initialization. { tile.setDefaultBounds2D(bounds); } /** The paintable component to hold the tile. */ PaintableComponent tileComponent = new PaintableComponent(tile); // The tile component initialization. { tileComponent.setBackground(Colors.white); tileComponent.setOpaque(true); tileComponent.setBorder(Borders.line(2)); } // The points XPoint2D P0 = new XPoint2D(100, 400); XPoint2D P1 = new XPoint2D( 50, 50); XPoint2D P2 = new XPoint2D(450, 200); XPoint2D P3 = new XPoint2D(350, 450); XPoint2D Q0 = new XPoint2D(); XPoint2D Q1 = new XPoint2D(); XPoint2D Q2 = new XPoint2D(); XPoint2D R0 = new XPoint2D(); XPoint2D R1 = new XPoint2D(); XPoint2D S0 = new XPoint2D(); // The paints Paint paint0 = Colors.red; Paint paint1 = Colors.mediumvioletred; Paint paint2 = Colors.blueviolet; Paint paint3 = Colors.lightskyblue; /** The error for mouse hits. */ int epsilon = 5; /** The plotmark for the points. */ PlotMark mark = new PlotMark(PlotMark.FILLED_SQUARE, epsilon); /** The stroke for the cubic and the lines. */ BasicStroke stroke = new BasicStroke(2); /** The label font. */ Font font = null; /** The label locator strategy. */ TextAnchor.Locator locator = TextAnchor.CENTER_ASCENTLINE; /** True if mouse position in vertex. */ boolean inVertex = false; /** The vertex for the mouse point. */ XPoint2D vertex; /** The mouse pressed action. */ private MouseAction mousePressed = new MouseAction() { public void mouseActionPerformed(MouseEvent evt) { mousePressed(evt); } }; /** The mouse dragged action. */ private MouseAction mouseDragged = new MouseAction() { public void mouseActionPerformed(MouseEvent evt) { mouseDragged(evt); } }; /** The action to paint the structure. */ private SimpleAction paintStructure = new SimpleAction("Paint Structure") { public void perform() { paintStructure(); } }; /** The view to determine whether to show labels. */ BooleanView showLabels = new BooleanView("Show Point Labels", paintStructure, true); /** The panel gap. */ int gap = 10; /** The stuff for the structure panel. */ Object[] structureStuff = { tileComponent, showLabels }; /** The structure panel. */ VTable structurePanel = new VTable(structureStuff, gap, gap, CENTER); /** The annotation array for P row in data panel. */ Annotation[] PRow = new Annotation[12]; /** The annotation array for Q row in data panel. */ Annotation[] QRow = new Annotation[9]; /** The annotation array for R row in data panel. */ Annotation[] RRow = new Annotation[6]; /** The annotation array for S row in data panel. */ Annotation[] SRow = new Annotation[3]; /** The cubic data panel. */ TablePanel dataPanel = new TablePanel(10, 3, gap, gap, CENTER); { dataPanel.emptyBorder(8); dataPanel.lineBorder(2); } /** * The tabbed pane to hold the cubic structure tab * and the cubic data tab. */ JTabbedPane tabbedPane = new JTabbedPane(); // The tabbed pane initialization. { tabbedPane.add("Cubic Structure", structurePanel); tabbedPane.add("Cubic Data", dataPanel ); } /** The constructor to complete initialization. */ public CubicSubdivision() { add(tabbedPane); setLabelFont(); initDataPanel(); paintStructure(); addMouseActions(); } /** Set the font for the labels. */ void setLabelFont() { String name = Fonts.getMonospacedFontFamilyName(); int fontsize = 20; font = new Font(name, Font.BOLD, fontsize); } void addMouseActions() { MouseActionAdapter adapter = tileComponent.getMouseActionAdapter(); adapter.addMousePressedAction(mousePressed); adapter.addMouseDraggedAction(mouseDragged); } /** * Perform the midpoint subdivision of the Bezier frame * defined by P0, P1, P2, P3. Place the data into * Q0, Q1, Q2, R0, R1, S0. */ void subdivide() { Q0.x = (P0.x + P1.x) / 2; Q0.y = (P0.y + P1.y) / 2; Q1.x = (P1.x + P2.x) / 2; Q1.y = (P1.y + P2.y) / 2; Q2.x = (P2.x + P3.x) / 2; Q2.y = (P2.y + P3.y) / 2; R0.x = (Q0.x + Q1.x) / 2; R0.y = (Q0.y + Q1.y) / 2; R1.x = (Q1.x + Q2.x) / 2; R1.y = (Q1.y + Q2.y) / 2; S0.x = (R0.x + R1.x) / 2; S0.y = (R0.y + R1.y) / 2; } /** Paint the cubic structure. */ void paintStructure() { subdivide(); sequence = new PaintableSequence(); appendLabels(); appendPoints(); appendCubic(); appendLines(); tile.setPaintable(sequence); updateDataPanel(); } /** Append the point labels to the sequence. */ void appendLabels() { if (! showLabels()) return; appendLabel("P0", P0, 10, paint0); appendLabel("P1", P1, 10, paint0); appendLabel("P2", P2, 10, paint0); appendLabel("P3", P3, 10, paint0); appendLabel("Q0", Q0, 10, paint1); appendLabel("Q1", Q1, 10, paint1); appendLabel("Q2", Q2, 10, paint1); appendLabel("R0", R0, 10, paint2); appendLabel("R1", R1, 10, paint2); appendLabel("S0", S0, 10, paint3); } /** Append the points to the sequence. */ void appendPoints() { PointPaintable p0 = new PointPaintable(P0, mark, paint0); PointPaintable p1 = new PointPaintable(P1, mark, paint0); PointPaintable p2 = new PointPaintable(P2, mark, paint0); PointPaintable p3 = new PointPaintable(P3, mark, paint0); PointPaintable q0 = new PointPaintable(Q0, mark, paint1); PointPaintable q1 = new PointPaintable(Q1, mark, paint1); PointPaintable q2 = new PointPaintable(Q2, mark, paint1); PointPaintable r0 = new PointPaintable(R0, mark, paint2); PointPaintable r1 = new PointPaintable(R1, mark, paint2); PointPaintable s0 = new PointPaintable(S0, mark, paint3); sequence.appendPaintable(p0); sequence.appendPaintable(p1); sequence.appendPaintable(p2); sequence.appendPaintable(p3); sequence.appendPaintable(q0); sequence.appendPaintable(q1); sequence.appendPaintable(q2); sequence.appendPaintable(r0); sequence.appendPaintable(r1); sequence.appendPaintable(s0); } /** Append the cubic curve to the sequence. */ void appendCubic() { float x0 = (float) P0.x; float y0 = (float) P0.y; float x1 = (float) P1.x; float y1 = (float) P1.y; float x2 = (float) P2.x; float y2 = (float) P2.y; float x3 = (float) P3.x; float y3 = (float) P3.y; GeneralPath path = new GeneralPath(); path.moveTo(x0, y0); path.curveTo(x1, y1, x2, y2, x3, y3); ShapePaintable cubic = new ShapePaintable (path, PaintMode.DRAW, null, Colors.black, stroke); sequence.appendPaintable(cubic); } /** Append the lines to the sequence. */ void appendLines() { appendLine(P0, P1, paint0); appendLine(P1, P2, paint0); appendLine(P2, P3, paint0); appendLine(Q0, Q1, paint1); appendLine(Q1, Q2, paint1); appendLine(R0, R1, paint2); } /** Whether to show point labels. */ boolean showLabels() { return showLabels.getBooleanValue(); } /** * Append the label * below the given point * by the given amount delta * using the given paint. */ void appendLabel (String label, XPoint2D a, double delta, Paint paint) { float x = (float) a.x; float y = (float) (a.y + delta); TextPaintable tp = new TextPaintable (label, font, paint, locator, x, y); sequence.appendPaintable(tp); } void appendLine(XPoint2D a, XPoint2D b, Paint paint) { XLine2D line = new XLine2D(a.x, a.y, b.x, b.y); ShapePaintable sp = new ShapePaintable (line, PaintMode.DRAW, null, paint, stroke); sequence.appendPaintable(sp); } /** The mouse pressed behavior. */ void mousePressed(MouseEvent evt) { vertex = null; int x = evt.getX(); int y = evt.getY(); if (Metric.MAX.isNear(P0.x, P0.y, x, y, epsilon)) vertex = P0; else if (Metric.MAX.isNear(P1.x, P1.y, x, y, epsilon)) vertex = P1; else if (Metric.MAX.isNear(P2.x, P2.y, x, y, epsilon)) vertex = P2; else if (Metric.MAX.isNear(P3.x, P3.y, x, y, epsilon)) vertex = P3; inVertex = (vertex != null); } /** The mouse dragged behavior. */ void mouseDragged(MouseEvent evt) { if (inVertex) { vertex.x = evt.getX(); vertex.y = evt.getY(); paintStructure(); } } /** * Initialize the labels in the data panel * and set minimum column widths. */ void initDataPanel() { for (int i = 0; i < 12; i++) { PRow[i] = new Annotation("", font); int row = i / 3; int col = i % 3; dataPanel.addObject(PRow[i], row, col); } for (int i = 0; i < 9; i++) { QRow[i] = new Annotation("", font); int row = i / 3 + 4; int col = i % 3; dataPanel.addObject(QRow[i], row, col); } for (int i = 0; i < 6; i++) { RRow[i] = new Annotation("", font); int row = i / 3 + 7; int col = i % 3; dataPanel.addObject(RRow[i], row, col); } for (int i = 0; i < 3; i++) { SRow[i] = new Annotation("", font); int row = i / 3 + 9; int col = i % 3; dataPanel.addObject(SRow[i], row, col); } int width = 120; for (int i = 0; i < 3; i++) dataPanel.setMinimumColumnWidth(i, width); PRow[0].setText("P0"); PRow[3].setText("P1"); PRow[6].setText("P2"); PRow[9].setText("P3"); QRow[0].setText("Q0"); QRow[3].setText("Q1"); QRow[6].setText("Q2"); RRow[0].setText("R0"); RRow[3].setText("R1"); SRow[0].setText("S0"); dataPanel.setBackground(Colors.gold); } /** Update the coordinate data in the data panel. */ void updateDataPanel() { PRow[1].setText(P0.x + ""); PRow[2].setText(P0.y + ""); PRow[4].setText(P1.x + ""); PRow[5].setText(P1.y + ""); PRow[7].setText(P2.x + ""); PRow[8].setText(P2.y + ""); PRow[10].setText(P3.x + ""); PRow[11].setText(P3.y + ""); QRow[1].setText(Q0.x + ""); QRow[2].setText(Q0.y + ""); QRow[4].setText(Q1.x + ""); QRow[5].setText(Q1.y + ""); QRow[7].setText(Q2.x + ""); QRow[8].setText(Q2.y + ""); RRow[1].setText(R0.x + ""); RRow[2].setText(R0.y + ""); RRow[4].setText(R1.x + ""); RRow[5].setText(R1.y + ""); SRow[1].setText(S0.x + ""); SRow[2].setText(S0.y + ""); } /** * The main program to launch the cubic subdivision demo. * * @param args ignored */ public static void main(String[] args) { new CubicSubdivision().frame("Cubic Subdivision"); } }