| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.commons.math4.userguide.geometry; |
| |
| import java.awt.BorderLayout; |
| import java.awt.Color; |
| import java.awt.Component; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.geom.Point2D; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import javax.swing.BorderFactory; |
| import javax.swing.JButton; |
| import javax.swing.JComponent; |
| import javax.swing.JPanel; |
| import javax.swing.JSplitPane; |
| |
| |
| import org.apache.commons.rng.UniformRandomProvider; |
| import org.apache.commons.rng.simple.RandomSource; |
| import org.apache.commons.geometry.core.precision.EpsilonDoublePrecisionContext; |
| import org.apache.commons.geometry.euclidean.twod.Segment; |
| import org.apache.commons.geometry.euclidean.twod.Vector2D; |
| import org.apache.commons.geometry.enclosing.Encloser; |
| import org.apache.commons.geometry.enclosing.EnclosingBall; |
| import org.apache.commons.geometry.enclosing.WelzlEncloser; |
| import org.apache.commons.geometry.enclosing.euclidean.twod.DiskGenerator; |
| import org.apache.commons.geometry.enclosing.euclidean.twod.WelzlEncloser2D; |
| import org.apache.commons.geometry.hull.euclidean.twod.ConvexHull2D; |
| import org.apache.commons.geometry.hull.euclidean.twod.ConvexHullGenerator2D; |
| import org.apache.commons.geometry.hull.euclidean.twod.MonotoneChain; |
| |
| import org.apache.commons.math4.util.FastMath; |
| import org.apache.commons.math4.userguide.ExampleUtils; |
| import org.apache.commons.math4.userguide.ExampleUtils.ExampleFrame; |
| import org.piccolo2d.PCamera; |
| import org.piccolo2d.PCanvas; |
| import org.piccolo2d.PNode; |
| import org.piccolo2d.event.PBasicInputEventHandler; |
| import org.piccolo2d.event.PInputEvent; |
| import org.piccolo2d.event.PMouseWheelZoomEventHandler; |
| import org.piccolo2d.nodes.PPath; |
| import org.piccolo2d.nodes.PText; |
| |
| /** |
| * Simple example illustrating some parts of the geometry package. |
| * |
| * TODO: |
| * - select tolerance level |
| * - allow editing of the point set |
| */ |
| public class GeometryExample { |
| |
| public static List<Vector2D> createRandomPoints(int size) { |
| final UniformRandomProvider random = RandomSource.create(RandomSource.MT); |
| |
| // create the cloud container |
| List<Vector2D> points = new ArrayList<>(size); |
| // fill the cloud with a random distribution of points |
| for (int i = 0; i < size; i++) { |
| points.add(Vector2D.of(FastMath.round(random.nextDouble() * 400 + 100), |
| FastMath.round(random.nextDouble() * 400 + 100))); |
| } |
| |
| return points; |
| } |
| |
| public static List<Vector2D> createCircle(int samples) { |
| List<Vector2D> points = new ArrayList<>(); |
| final Vector2D center = Vector2D.of(300, 300); |
| double range = 2.0 * FastMath.PI; |
| double step = range / (samples + 1); |
| for (double angle = 0; angle < range; angle += step) { |
| Vector2D circle = Vector2D.of(FastMath.cos(angle), FastMath.sin(angle)); |
| points.add(circle.multiply(200).add(center)); |
| } |
| |
| return points; |
| } |
| |
| public static List<Vector2D> createCross() { |
| List<Vector2D> points = new ArrayList<>(); |
| |
| for (int i = 100; i < 500; i += 10) { |
| points.add(Vector2D.of(300, i)); |
| points.add(Vector2D.of(i, 300)); |
| } |
| |
| return points; |
| } |
| |
| public static PCanvas createCanvas() { |
| final PCanvas canvas = new PCanvas(); |
| final PCamera camera = canvas.getCamera(); |
| |
| final PText tooltipNode = new PText(); |
| tooltipNode.setPickable(false); |
| camera.addChild(tooltipNode); |
| |
| camera.addInputEventListener(new PBasicInputEventHandler() { |
| public void mouseMoved(final PInputEvent event) { |
| updateToolTip(event); |
| } |
| |
| public void mouseDragged(final PInputEvent event) { |
| updateToolTip(event); |
| } |
| |
| public void updateToolTip(final PInputEvent event) { |
| final PNode n = event.getPickedNode(); |
| final Object object = (Object) n.getAttribute("tooltip"); |
| if (object != null) { |
| final String tooltipString = object.toString(); |
| final Point2D p = event.getCanvasPosition(); |
| |
| event.getPath().canvasToLocal(p, camera); |
| |
| tooltipNode.setText(tooltipString); |
| tooltipNode.setOffset(p.getX() + 8, p.getY() - 8); |
| } else { |
| tooltipNode.setText(null); |
| } |
| } |
| }); |
| |
| // uninstall default zoom event handler |
| canvas.removeInputEventListener(canvas.getZoomEventHandler()); |
| |
| // install mouse wheel zoom event handler |
| final PMouseWheelZoomEventHandler mouseWheelZoomEventHandler = new PMouseWheelZoomEventHandler(); |
| canvas.addInputEventListener(mouseWheelZoomEventHandler); |
| |
| return canvas; |
| } |
| |
| @SuppressWarnings("serial") |
| public static class Display extends ExampleFrame { |
| |
| private List<Vector2D> points; |
| private PCanvas canvas; |
| private JComponent container; |
| private JComponent controlPanel; |
| |
| public Display() { |
| setTitle("Commons Math: Geometry Examples"); |
| setSize(800, 700); |
| |
| container = new JPanel(new BorderLayout()); |
| canvas = createCanvas(); |
| container.add(canvas); |
| container.setBorder(BorderFactory.createLineBorder(Color.black, 1)); |
| |
| controlPanel = new JPanel(); |
| JButton random = new JButton("Randomize"); |
| controlPanel.add(random); |
| |
| random.addActionListener(new ActionListener() { |
| |
| // @Override |
| public void actionPerformed(ActionEvent e) { |
| canvas.getLayer().removeAllChildren(); |
| |
| points = createRandomPoints(1000); |
| paintConvexHull(); |
| } |
| }); |
| |
| JButton circle = new JButton("Circle"); |
| controlPanel.add(circle); |
| |
| circle.addActionListener(new ActionListener() { |
| |
| // @Override |
| public void actionPerformed(ActionEvent e) { |
| canvas.getLayer().removeAllChildren(); |
| |
| points = createCircle(100); |
| paintConvexHull(); |
| } |
| }); |
| |
| JButton cross = new JButton("Cross"); |
| controlPanel.add(cross); |
| |
| cross.addActionListener(new ActionListener() { |
| |
| // @Override |
| public void actionPerformed(ActionEvent e) { |
| canvas.getLayer().removeAllChildren(); |
| |
| points = createCross(); |
| paintConvexHull(); |
| } |
| }); |
| |
| JSplitPane splitpane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, container, controlPanel); |
| splitpane.setDividerLocation(600); |
| |
| add(splitpane); |
| |
| points = createRandomPoints(1000); |
| paintConvexHull(); |
| } |
| |
| @Override |
| public Component getMainPanel() { |
| return container; |
| } |
| |
| public void paintConvexHull() { |
| PNode pointSet = new PNode(); |
| for (Vector2D point : points) { |
| final PNode node = PPath.createEllipse(point.getX() - 1, point.getY() - 1, 2, 2); |
| node.addAttribute("tooltip", point); |
| node.setPaint(Color.gray); |
| pointSet.addChild(node); |
| } |
| |
| canvas.getLayer().addChild(pointSet); |
| |
| ConvexHullGenerator2D generator = new MonotoneChain(true, new EpsilonDoublePrecisionContext(1e-6)); |
| ConvexHull2D hull = generator.generate(points); //AklToussaintHeuristic.reducePoints(points)); |
| |
| PNode hullNode = new PNode(); |
| for (Vector2D vertex : hull.getVertices()) { |
| final PPath node = PPath.createEllipse(vertex.getX() - 1, vertex.getY() - 1, 2, 2); |
| node.addAttribute("tooltip", vertex); |
| node.setPaint(Color.red); |
| node.setStrokePaint(Color.red); |
| hullNode.addChild(node); |
| } |
| |
| for (Segment line : hull.getPath().getSegments()) { |
| final PPath node = PPath.createLine(line.getStartPoint().getX(), line.getStartPoint().getY(), |
| line.getEndPoint().getX(), line.getEndPoint().getY()); |
| node.setPickable(false); |
| node.setPaint(Color.red); |
| node.setStrokePaint(Color.red); |
| hullNode.addChild(node); |
| } |
| |
| canvas.getLayer().addChild(hullNode); |
| |
| WelzlEncloser2D encloser = new WelzlEncloser2D(new EpsilonDoublePrecisionContext(1e-10)); |
| EnclosingBall<Vector2D> ball = encloser.enclose(points); |
| |
| final double radius = ball.getRadius(); |
| PPath ballCenter = |
| PPath.createEllipse(ball.getCenter().getX() - 1, ball.getCenter().getY() - 1, 2, 2); |
| ballCenter.setStrokePaint(Color.blue); |
| ballCenter.setPaint(Color.blue); |
| canvas.getLayer().addChild(0, ballCenter); |
| |
| PPath ballNode = |
| PPath.createEllipse(ball.getCenter().getX() - radius, ball.getCenter().getY() - radius, |
| radius * 2, radius * 2); |
| ballNode.setTransparency(1.0f); |
| ballNode.setStrokePaint(Color.blue); |
| canvas.getLayer().addChild(0, ballNode); |
| } |
| } |
| |
| public static void main(final String[] argv) { |
| ExampleUtils.showExampleFrame(new Display()); |
| } |
| } |