toxiclibs 0.2-java → 0.5.0-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +16 -0
- data/CHANGELOG.md +4 -0
- data/LICENSE +675 -0
- data/README.md +12 -5
- data/Rakefile +25 -82
- data/examples/attract_repel/attract_repel.rb +30 -0
- data/examples/attract_repel/attractor.rb +23 -0
- data/examples/attract_repel/particle.rb +27 -0
- data/examples/data/ti_yong.png +0 -0
- data/examples/force_directed/cluster.rb +76 -0
- data/examples/force_directed/force_directed_graph.rb +92 -0
- data/examples/force_directed/node.rb +26 -0
- data/examples/gray_scott_image.rb +75 -0
- data/examples/gray_scott_tone_map.rb +77 -0
- data/examples/implicit.rb +139 -0
- data/examples/inflate_mesh.rb +89 -0
- data/examples/model_align.rb +44 -0
- data/examples/povmesh/ftest.rb +46 -0
- data/examples/povmesh/tentacle.rb +73 -0
- data/examples/simple_cluster/cluster.rb +47 -0
- data/examples/simple_cluster/node.rb +27 -0
- data/examples/simple_cluster/simple_cluster.rb +60 -0
- data/examples/soft_body/blanket.rb +45 -0
- data/examples/soft_body/connection.rb +16 -0
- data/examples/soft_body/particle.rb +22 -0
- data/examples/soft_body/soft_body_square_adapted.rb +55 -0
- data/lib/toxiclibs.jar +0 -0
- data/lib/toxiclibs.rb +91 -32
- data/lib/toxiclibs/version.rb +1 -1
- data/pom.xml +122 -0
- data/src/com/toxi/net/ClientListener.java +41 -0
- data/src/com/toxi/net/ServerListener.java +70 -0
- data/src/com/toxi/net/ServerListenerAdapter.java +47 -0
- data/src/com/toxi/net/ServerState.java +18 -0
- data/src/com/toxi/net/UDPConnection.java +66 -0
- data/src/com/toxi/net/UDPSyncClient.java +81 -0
- data/src/com/toxi/net/UDPSyncServer.java +450 -0
- data/src/com/toxi/nio/UDPClient.java +121 -0
- data/src/com/toxi/nio/UDPClientState.java +32 -0
- data/src/com/toxi/nio/UDPServer.java +129 -0
- data/src/toxi/color/AccessCriteria.java +114 -0
- data/src/toxi/color/AlphaAccessor.java +67 -0
- data/src/toxi/color/CMYKAccessor.java +122 -0
- data/src/toxi/color/CMYKDistanceProxy.java +40 -0
- data/src/toxi/color/ColorGradient.java +260 -0
- data/src/toxi/color/ColorList.java +699 -0
- data/src/toxi/color/ColorRange.java +671 -0
- data/src/toxi/color/ColorTheme.java +163 -0
- data/src/toxi/color/DistanceProxy.java +44 -0
- data/src/toxi/color/HSVAccessor.java +113 -0
- data/src/toxi/color/HSVDistanceProxy.java +40 -0
- data/src/toxi/color/HistEntry.java +85 -0
- data/src/toxi/color/Histogram.java +185 -0
- data/src/toxi/color/Hue.java +249 -0
- data/src/toxi/color/LuminanceAccessor.java +78 -0
- data/src/toxi/color/NamedColor.java +935 -0
- data/src/toxi/color/ProximityComparator.java +70 -0
- data/src/toxi/color/RGBAccessor.java +113 -0
- data/src/toxi/color/RGBDistanceProxy.java +41 -0
- data/src/toxi/color/ReadonlyTColor.java +296 -0
- data/src/toxi/color/TColor.java +1677 -0
- data/src/toxi/color/TColorAdapter.java +68 -0
- data/src/toxi/color/ToneMap.java +218 -0
- data/src/toxi/color/theory/AnalogousStrategy.java +140 -0
- data/src/toxi/color/theory/ColorTheoryRegistry.java +139 -0
- data/src/toxi/color/theory/ColorTheoryStrategy.java +56 -0
- data/src/toxi/color/theory/ComplementaryStrategy.java +111 -0
- data/src/toxi/color/theory/CompoundTheoryStrategy.java +143 -0
- data/src/toxi/color/theory/LeftSplitComplementaryStrategy.java +82 -0
- data/src/toxi/color/theory/MonochromeTheoryStrategy.java +103 -0
- data/src/toxi/color/theory/RightSplitComplementaryStrategy.java +82 -0
- data/src/toxi/color/theory/SingleComplementStrategy.java +76 -0
- data/src/toxi/color/theory/SplitComplementaryStrategy.java +77 -0
- data/src/toxi/color/theory/TetradTheoryStrategy.java +114 -0
- data/src/toxi/color/theory/TriadTheoryStrategy.java +77 -0
- data/src/toxi/data/csv/CSVAdapter.java +74 -0
- data/src/toxi/data/csv/CSVFieldMapper.java +212 -0
- data/src/toxi/data/csv/CSVListener.java +61 -0
- data/src/toxi/data/csv/CSVParser.java +202 -0
- data/src/toxi/data/feeds/AtomAuthor.java +49 -0
- data/src/toxi/data/feeds/AtomContent.java +50 -0
- data/src/toxi/data/feeds/AtomEntry.java +111 -0
- data/src/toxi/data/feeds/AtomFeed.java +129 -0
- data/src/toxi/data/feeds/AtomLink.java +62 -0
- data/src/toxi/data/feeds/RSSChannel.java +88 -0
- data/src/toxi/data/feeds/RSSEnclosure.java +60 -0
- data/src/toxi/data/feeds/RSSFeed.java +99 -0
- data/src/toxi/data/feeds/RSSItem.java +104 -0
- data/src/toxi/data/feeds/util/EntityStripper.java +2480 -0
- data/src/toxi/data/feeds/util/Iso8601DateAdapter.java +101 -0
- data/src/toxi/data/feeds/util/Rfc822DateAdapter.java +93 -0
- data/src/toxi/geom/AABB.java +658 -0
- data/src/toxi/geom/Axis3D.java +116 -0
- data/src/toxi/geom/AxisAlignedCylinder.java +163 -0
- data/src/toxi/geom/BernsteinPolynomial.java +94 -0
- data/src/toxi/geom/BezierCurve2D.java +159 -0
- data/src/toxi/geom/BezierCurve3D.java +148 -0
- data/src/toxi/geom/BooleanShapeBuilder.java +185 -0
- data/src/toxi/geom/BoxIntersector.java +52 -0
- data/src/toxi/geom/Circle.java +230 -0
- data/src/toxi/geom/CircleIntersector.java +85 -0
- data/src/toxi/geom/Cone.java +150 -0
- data/src/toxi/geom/ConvexPolygonClipper.java +136 -0
- data/src/toxi/geom/CoordinateExtractor.java +16 -0
- data/src/toxi/geom/Ellipse.java +250 -0
- data/src/toxi/geom/GMatrix.java +2599 -0
- data/src/toxi/geom/GVector.java +833 -0
- data/src/toxi/geom/GlobalGridTesselator.java +54 -0
- data/src/toxi/geom/GridTesselator.java +108 -0
- data/src/toxi/geom/Intersector2D.java +49 -0
- data/src/toxi/geom/Intersector3D.java +51 -0
- data/src/toxi/geom/IsectData2D.java +103 -0
- data/src/toxi/geom/IsectData3D.java +103 -0
- data/src/toxi/geom/Line2D.java +534 -0
- data/src/toxi/geom/Line3D.java +471 -0
- data/src/toxi/geom/LineStrip2D.java +430 -0
- data/src/toxi/geom/LineStrip3D.java +230 -0
- data/src/toxi/geom/LocalGridTesselator.java +57 -0
- data/src/toxi/geom/Matrix3d.java +3048 -0
- data/src/toxi/geom/Matrix4f.java +3446 -0
- data/src/toxi/geom/Matrix4x4.java +1076 -0
- data/src/toxi/geom/MatrixSizeException.java +58 -0
- data/src/toxi/geom/OctreeVisitor.java +44 -0
- data/src/toxi/geom/Origin3D.java +148 -0
- data/src/toxi/geom/Plane.java +293 -0
- data/src/toxi/geom/PlaneIntersector.java +57 -0
- data/src/toxi/geom/PointCloud3D.java +253 -0
- data/src/toxi/geom/PointOctree.java +502 -0
- data/src/toxi/geom/PointQuadtree.java +375 -0
- data/src/toxi/geom/Polygon2D.java +1038 -0
- data/src/toxi/geom/PolygonClipper2D.java +45 -0
- data/src/toxi/geom/PolygonTesselator.java +20 -0
- data/src/toxi/geom/QuadtreeVisitor.java +44 -0
- data/src/toxi/geom/Quaternion.java +641 -0
- data/src/toxi/geom/Ray2D.java +146 -0
- data/src/toxi/geom/Ray3D.java +150 -0
- data/src/toxi/geom/Ray3DIntersector.java +75 -0
- data/src/toxi/geom/ReadonlyVec2D.java +575 -0
- data/src/toxi/geom/ReadonlyVec3D.java +628 -0
- data/src/toxi/geom/ReadonlyVec4D.java +431 -0
- data/src/toxi/geom/Rect.java +720 -0
- data/src/toxi/geom/Reflector3D.java +58 -0
- data/src/toxi/geom/Shape2D.java +94 -0
- data/src/toxi/geom/Shape3D.java +42 -0
- data/src/toxi/geom/SingularMatrixException.java +57 -0
- data/src/toxi/geom/SpatialBins.java +182 -0
- data/src/toxi/geom/SpatialIndex.java +61 -0
- data/src/toxi/geom/Sphere.java +224 -0
- data/src/toxi/geom/SphereIntersectorReflector.java +196 -0
- data/src/toxi/geom/Spline2D.java +349 -0
- data/src/toxi/geom/Spline3D.java +351 -0
- data/src/toxi/geom/SutherlandHodgemanClipper.java +151 -0
- data/src/toxi/geom/Triangle2D.java +422 -0
- data/src/toxi/geom/Triangle3D.java +456 -0
- data/src/toxi/geom/TriangleIntersector.java +105 -0
- data/src/toxi/geom/Vec2D.java +1328 -0
- data/src/toxi/geom/Vec3D.java +1832 -0
- data/src/toxi/geom/Vec4D.java +985 -0
- data/src/toxi/geom/VecMathUtil.java +100 -0
- data/src/toxi/geom/XAxisCylinder.java +64 -0
- data/src/toxi/geom/YAxisCylinder.java +65 -0
- data/src/toxi/geom/ZAxisCylinder.java +64 -0
- data/src/toxi/geom/mesh/BezierPatch.java +200 -0
- data/src/toxi/geom/mesh/BoxSelector.java +62 -0
- data/src/toxi/geom/mesh/DefaultSTLColorModel.java +67 -0
- data/src/toxi/geom/mesh/DefaultSelector.java +50 -0
- data/src/toxi/geom/mesh/Face.java +176 -0
- data/src/toxi/geom/mesh/LaplacianSmooth.java +80 -0
- data/src/toxi/geom/mesh/MaterialiseSTLColorModel.java +150 -0
- data/src/toxi/geom/mesh/Mesh3D.java +224 -0
- data/src/toxi/geom/mesh/MeshIntersector.java +91 -0
- data/src/toxi/geom/mesh/OBJWriter.java +194 -0
- data/src/toxi/geom/mesh/PLYWriter.java +167 -0
- data/src/toxi/geom/mesh/PlaneSelector.java +90 -0
- data/src/toxi/geom/mesh/STLColorModel.java +54 -0
- data/src/toxi/geom/mesh/STLReader.java +185 -0
- data/src/toxi/geom/mesh/STLWriter.java +323 -0
- data/src/toxi/geom/mesh/SphereFunction.java +156 -0
- data/src/toxi/geom/mesh/SphericalHarmonics.java +110 -0
- data/src/toxi/geom/mesh/SuperEllipsoid.java +110 -0
- data/src/toxi/geom/mesh/SurfaceFunction.java +75 -0
- data/src/toxi/geom/mesh/SurfaceMeshBuilder.java +149 -0
- data/src/toxi/geom/mesh/Terrain.java +451 -0
- data/src/toxi/geom/mesh/TriangleMesh.java +1201 -0
- data/src/toxi/geom/mesh/Vertex.java +78 -0
- data/src/toxi/geom/mesh/VertexSelector.java +193 -0
- data/src/toxi/geom/mesh/WEFace.java +100 -0
- data/src/toxi/geom/mesh/WEMeshFilterStrategy.java +51 -0
- data/src/toxi/geom/mesh/WETriangleMesh.java +761 -0
- data/src/toxi/geom/mesh/WEVertex.java +134 -0
- data/src/toxi/geom/mesh/WingedEdge.java +115 -0
- data/src/toxi/geom/mesh/subdiv/CentroidSubdiv.java +37 -0
- data/src/toxi/geom/mesh/subdiv/DisplacementSubdivision.java +85 -0
- data/src/toxi/geom/mesh/subdiv/DualDisplacementSubdivision.java +94 -0
- data/src/toxi/geom/mesh/subdiv/DualSubdivision.java +49 -0
- data/src/toxi/geom/mesh/subdiv/EdgeLengthComparator.java +50 -0
- data/src/toxi/geom/mesh/subdiv/FaceCountComparator.java +51 -0
- data/src/toxi/geom/mesh/subdiv/MidpointDisplacementSubdivision.java +80 -0
- data/src/toxi/geom/mesh/subdiv/MidpointSubdiv.java +42 -0
- data/src/toxi/geom/mesh/subdiv/MidpointSubdivision.java +48 -0
- data/src/toxi/geom/mesh/subdiv/NewSubdivStrategy.java +23 -0
- data/src/toxi/geom/mesh/subdiv/NormalDisplacementSubdivision.java +74 -0
- data/src/toxi/geom/mesh/subdiv/SubdivisionStrategy.java +83 -0
- data/src/toxi/geom/mesh/subdiv/TriSubdivision.java +51 -0
- data/src/toxi/geom/mesh2d/DelaunayTriangle.java +222 -0
- data/src/toxi/geom/mesh2d/DelaunayTriangulation.java +327 -0
- data/src/toxi/geom/mesh2d/DelaunayVertex.java +560 -0
- data/src/toxi/geom/mesh2d/Voronoi.java +149 -0
- data/src/toxi/geom/nurbs/BasicNurbsCurve.java +210 -0
- data/src/toxi/geom/nurbs/BasicNurbsSurface.java +233 -0
- data/src/toxi/geom/nurbs/ControlNet.java +148 -0
- data/src/toxi/geom/nurbs/CurveCreator.java +112 -0
- data/src/toxi/geom/nurbs/CurveUtils.java +259 -0
- data/src/toxi/geom/nurbs/InterpolationException.java +65 -0
- data/src/toxi/geom/nurbs/KnotVector.java +333 -0
- data/src/toxi/geom/nurbs/NurbsCreator.java +815 -0
- data/src/toxi/geom/nurbs/NurbsCurve.java +120 -0
- data/src/toxi/geom/nurbs/NurbsMeshCreator.java +145 -0
- data/src/toxi/geom/nurbs/NurbsSurface.java +147 -0
- data/src/toxi/image/util/Filter8bit.java +331 -0
- data/src/toxi/image/util/TiledFrameExporter.java +162 -0
- data/src/toxi/math/BezierInterpolation.java +102 -0
- data/src/toxi/math/CircularInterpolation.java +88 -0
- data/src/toxi/math/CosineInterpolation.java +51 -0
- data/src/toxi/math/DecimatedInterpolation.java +77 -0
- data/src/toxi/math/ExponentialInterpolation.java +68 -0
- data/src/toxi/math/InterpolateStrategy.java +60 -0
- data/src/toxi/math/Interpolation2D.java +93 -0
- data/src/toxi/math/LinearInterpolation.java +46 -0
- data/src/toxi/math/MathUtils.java +990 -0
- data/src/toxi/math/NonLinearScaleMap.java +101 -0
- data/src/toxi/math/ScaleMap.java +183 -0
- data/src/toxi/math/SigmoidInterpolation.java +78 -0
- data/src/toxi/math/SinCosLUT.java +141 -0
- data/src/toxi/math/ThresholdInterpolation.java +58 -0
- data/src/toxi/math/ZoomLensInterpolation.java +126 -0
- data/src/toxi/math/conversion/UnitTranslator.java +161 -0
- data/src/toxi/math/noise/PerlinNoise.java +281 -0
- data/src/toxi/math/noise/SimplexNoise.java +542 -0
- data/src/toxi/math/waves/AMFMSineWave.java +143 -0
- data/src/toxi/math/waves/AbstractWave.java +248 -0
- data/src/toxi/math/waves/ConstantWave.java +48 -0
- data/src/toxi/math/waves/FMHarmonicSquareWave.java +155 -0
- data/src/toxi/math/waves/FMSawtoothWave.java +144 -0
- data/src/toxi/math/waves/FMSineWave.java +142 -0
- data/src/toxi/math/waves/FMSquareWave.java +143 -0
- data/src/toxi/math/waves/FMTriangleWave.java +126 -0
- data/src/toxi/math/waves/SineWave.java +81 -0
- data/src/toxi/math/waves/Wave2D.java +68 -0
- data/src/toxi/math/waves/WaveState.java +69 -0
- data/src/toxi/music/scale/AbstractScale.java +117 -0
- data/src/toxi/music/scale/GenericScale.java +66 -0
- data/src/toxi/music/scale/MajorScale.java +41 -0
- data/src/toxi/newmesh/AttributedEdge.java +106 -0
- data/src/toxi/newmesh/AttributedFace.java +63 -0
- data/src/toxi/newmesh/IndexedTriangleMesh.java +809 -0
- data/src/toxi/newmesh/MeshAttributeCompiler.java +45 -0
- data/src/toxi/newmesh/MeshFaceNormalCompiler.java +52 -0
- data/src/toxi/newmesh/MeshUVCompiler.java +52 -0
- data/src/toxi/newmesh/MeshVertexColorCompiler.java +49 -0
- data/src/toxi/newmesh/MeshVertexCompiler.java +54 -0
- data/src/toxi/newmesh/MeshVertexNormalCompiler.java +55 -0
- data/src/toxi/newmesh/SpatialIndex.java +78 -0
- data/src/toxi/physics2d/ParticlePath2D.java +100 -0
- data/src/toxi/physics2d/ParticleString2D.java +184 -0
- data/src/toxi/physics2d/PullBackSpring2D.java +51 -0
- data/src/toxi/physics2d/VerletConstrainedSpring2D.java +89 -0
- data/src/toxi/physics2d/VerletMinDistanceSpring2D.java +57 -0
- data/src/toxi/physics2d/VerletParticle2D.java +457 -0
- data/src/toxi/physics2d/VerletPhysics2D.java +448 -0
- data/src/toxi/physics2d/VerletSpring2D.java +181 -0
- data/src/toxi/physics2d/behaviors/AttractionBehavior2D.java +212 -0
- data/src/toxi/physics2d/behaviors/ConstantForceBehavior2D.java +112 -0
- data/src/toxi/physics2d/behaviors/GravityBehavior2D.java +61 -0
- data/src/toxi/physics2d/behaviors/ParticleBehavior2D.java +66 -0
- data/src/toxi/physics2d/constraints/AngularConstraint.java +83 -0
- data/src/toxi/physics2d/constraints/AxisConstraint.java +71 -0
- data/src/toxi/physics2d/constraints/CircularConstraint.java +69 -0
- data/src/toxi/physics2d/constraints/MaxConstraint.java +66 -0
- data/src/toxi/physics2d/constraints/MinConstraint.java +66 -0
- data/src/toxi/physics2d/constraints/ParticleConstraint2D.java +47 -0
- data/src/toxi/physics2d/constraints/PolygonConstraint.java +93 -0
- data/src/toxi/physics2d/constraints/RectConstraint.java +114 -0
- data/src/toxi/physics3d/ParticlePath3D.java +100 -0
- data/src/toxi/physics3d/ParticleString3D.java +184 -0
- data/src/toxi/physics3d/PullBackSpring3D.java +50 -0
- data/src/toxi/physics3d/VerletConstrainedSpring3D.java +88 -0
- data/src/toxi/physics3d/VerletMinDistanceSpring3D.java +56 -0
- data/src/toxi/physics3d/VerletParticle3D.java +385 -0
- data/src/toxi/physics3d/VerletPhysics3D.java +417 -0
- data/src/toxi/physics3d/VerletSpring3D.java +180 -0
- data/src/toxi/physics3d/behaviors/AttractionBehavior3D.java +182 -0
- data/src/toxi/physics3d/behaviors/ConstantForceBehavior3D.java +92 -0
- data/src/toxi/physics3d/behaviors/GravityBehavior3D.java +61 -0
- data/src/toxi/physics3d/behaviors/ParticleBehavior3D.java +52 -0
- data/src/toxi/physics3d/constraints/AxisConstraint.java +68 -0
- data/src/toxi/physics3d/constraints/BoxConstraint.java +121 -0
- data/src/toxi/physics3d/constraints/CylinderConstraint.java +87 -0
- data/src/toxi/physics3d/constraints/MaxConstraint.java +65 -0
- data/src/toxi/physics3d/constraints/MinConstraint.java +65 -0
- data/src/toxi/physics3d/constraints/ParticleConstraint3D.java +49 -0
- data/src/toxi/physics3d/constraints/PlaneConstraint.java +78 -0
- data/src/toxi/physics3d/constraints/SoftBoxConstraint.java +87 -0
- data/src/toxi/physics3d/constraints/SphereConstraint.java +108 -0
- data/src/toxi/processing/ArrowModifier.java +116 -0
- data/src/toxi/processing/DashedLineModifier.java +48 -0
- data/src/toxi/processing/DeltaOrientationMapper.java +57 -0
- data/src/toxi/processing/Line2DRenderModifier.java +18 -0
- data/src/toxi/processing/MeshToVBO.java +94 -0
- data/src/toxi/processing/NormalMapper.java +18 -0
- data/src/toxi/processing/POVInterface.java +121 -0
- data/src/toxi/processing/POVMesh.java +219 -0
- data/src/toxi/processing/POVWriter.java +460 -0
- data/src/toxi/processing/RCOpaque.java +77 -0
- data/src/toxi/processing/RCTransp.java +78 -0
- data/src/toxi/processing/TextureBuilder.java +232 -0
- data/src/toxi/processing/Textures.java +110 -0
- data/src/toxi/processing/ToxiclibsSupport.java +1239 -0
- data/src/toxi/processing/Tracing.java +25 -0
- data/src/toxi/processing/XYZNormalMapper.java +30 -0
- data/src/toxi/sim/automata/CAMatrix.java +297 -0
- data/src/toxi/sim/automata/CARule.java +76 -0
- data/src/toxi/sim/automata/CARule2D.java +354 -0
- data/src/toxi/sim/automata/CAWolfram1D.java +309 -0
- data/src/toxi/sim/automata/EvolvableMatrix.java +61 -0
- data/src/toxi/sim/automata/MatrixEvolver.java +42 -0
- data/src/toxi/sim/dla/BottomUpOrder.java +76 -0
- data/src/toxi/sim/dla/DLA.java +497 -0
- data/src/toxi/sim/dla/DLAConfiguration.java +364 -0
- data/src/toxi/sim/dla/DLAEventAdapter.java +64 -0
- data/src/toxi/sim/dla/DLAEventListener.java +57 -0
- data/src/toxi/sim/dla/DLAGuideLines.java +219 -0
- data/src/toxi/sim/dla/DLAParticle.java +102 -0
- data/src/toxi/sim/dla/DLASegment.java +88 -0
- data/src/toxi/sim/dla/PipelineOrder.java +50 -0
- data/src/toxi/sim/dla/RadialDistanceOrder.java +92 -0
- data/src/toxi/sim/erosion/ErosionFunction.java +122 -0
- data/src/toxi/sim/erosion/TalusAngleErosion.java +145 -0
- data/src/toxi/sim/erosion/ThermalErosion.java +75 -0
- data/src/toxi/sim/fluids/FluidSolver2D.java +762 -0
- data/src/toxi/sim/fluids/FluidSolver3D.java +326 -0
- data/src/toxi/sim/grayscott/GrayScott.java +469 -0
- data/src/toxi/util/DateUtils.java +141 -0
- data/src/toxi/util/FileSequenceDescriptor.java +181 -0
- data/src/toxi/util/FileUtils.java +467 -0
- data/src/toxi/util/datatypes/ArraySet.java +128 -0
- data/src/toxi/util/datatypes/ArrayUtil.java +404 -0
- data/src/toxi/util/datatypes/BiasedDoubleRange.java +141 -0
- data/src/toxi/util/datatypes/BiasedFloatRange.java +141 -0
- data/src/toxi/util/datatypes/BiasedIntegerRange.java +141 -0
- data/src/toxi/util/datatypes/DoubleRange.java +251 -0
- data/src/toxi/util/datatypes/FloatRange.java +251 -0
- data/src/toxi/util/datatypes/GenericSet.java +215 -0
- data/src/toxi/util/datatypes/IntegerRange.java +247 -0
- data/src/toxi/util/datatypes/IntegerSet.java +149 -0
- data/src/toxi/util/datatypes/ItemIndex.java +72 -0
- data/src/toxi/util/datatypes/SingletonRegistry.java +91 -0
- data/src/toxi/util/datatypes/TypedProperties.java +291 -0
- data/src/toxi/util/datatypes/UndirectedGraph.java +134 -0
- data/src/toxi/util/datatypes/UniqueItemIndex.java +223 -0
- data/src/toxi/util/datatypes/WeightedRandomEntry.java +76 -0
- data/src/toxi/util/datatypes/WeightedRandomSet.java +125 -0
- data/src/toxi/util/events/EventDispatcher.java +86 -0
- data/src/toxi/volume/AdditiveBrush.java +19 -0
- data/src/toxi/volume/ArrayIsoSurface.java +297 -0
- data/src/toxi/volume/BoxBrush.java +100 -0
- data/src/toxi/volume/BrushMode.java +16 -0
- data/src/toxi/volume/HashIsoSurface.java +354 -0
- data/src/toxi/volume/IsoSurface.java +59 -0
- data/src/toxi/volume/MarchingCubesIndex.java +312 -0
- data/src/toxi/volume/MeshLatticeBuilder.java +358 -0
- data/src/toxi/volume/MeshVoxelizer.java +216 -0
- data/src/toxi/volume/MultiplyBrush.java +20 -0
- data/src/toxi/volume/PeakBrush.java +21 -0
- data/src/toxi/volume/ReplaceBrush.java +19 -0
- data/src/toxi/volume/RoundBrush.java +113 -0
- data/src/toxi/volume/VolumetricBrush.java +160 -0
- data/src/toxi/volume/VolumetricHashMap.java +179 -0
- data/src/toxi/volume/VolumetricSpace.java +195 -0
- data/src/toxi/volume/VolumetricSpaceArray.java +214 -0
- data/toxiclibs.gemspec +34 -0
- metadata +424 -27
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* __ .__ .__ ._____.
|
|
3
|
+
* _/ |_ _______ __|__| ____ | | |__\_ |__ ______
|
|
4
|
+
* \ __\/ _ \ \/ / |/ ___\| | | || __ \ / ___/
|
|
5
|
+
* | | ( <_> > <| \ \___| |_| || \_\ \\___ \
|
|
6
|
+
* |__| \____/__/\_ \__|\___ >____/__||___ /____ >
|
|
7
|
+
* \/ \/ \/ \/
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2006-2011 Karsten Schmidt
|
|
10
|
+
*
|
|
11
|
+
* This library is free software; you can redistribute it and/or
|
|
12
|
+
* modify it under the terms of the GNU Lesser General Public
|
|
13
|
+
* License as published by the Free Software Foundation; either
|
|
14
|
+
* version 2.1 of the License, or (at your option) any later version.
|
|
15
|
+
*
|
|
16
|
+
* http://creativecommons.org/licenses/LGPL/2.1/
|
|
17
|
+
*
|
|
18
|
+
* This library is distributed in the hope that it will be useful,
|
|
19
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
21
|
+
* Lesser General Public License for more details.
|
|
22
|
+
*
|
|
23
|
+
* You should have received a copy of the GNU Lesser General Public
|
|
24
|
+
* License along with this library; if not, write to the Free Software
|
|
25
|
+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
package toxi.geom;
|
|
29
|
+
|
|
30
|
+
import java.util.ArrayList;
|
|
31
|
+
import java.util.List;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Implements a spatial subdivision tree to work efficiently with large numbers
|
|
35
|
+
* of 2D particles. This quadtree can only be used for particle type objects and
|
|
36
|
+
* does NOT support 2D mesh geometry as other forms of quadtree might do.
|
|
37
|
+
*
|
|
38
|
+
* For further reference also see the QuadtreeDemo in the /examples folder.
|
|
39
|
+
*
|
|
40
|
+
*/
|
|
41
|
+
public class PointQuadtree extends Rect implements SpatialIndex<Vec2D> {
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
*
|
|
45
|
+
*/
|
|
46
|
+
public enum Type {
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
EMPTY,
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
*
|
|
55
|
+
*/
|
|
56
|
+
BRANCH,
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
*
|
|
60
|
+
*/
|
|
61
|
+
LEAF;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private PointQuadtree parent;
|
|
65
|
+
private PointQuadtree childNW, childNE, childSW, childSE;
|
|
66
|
+
|
|
67
|
+
private Type type;
|
|
68
|
+
|
|
69
|
+
private Vec2D value;
|
|
70
|
+
private float mx, my;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
*
|
|
74
|
+
* @param x
|
|
75
|
+
* @param y
|
|
76
|
+
* @param w
|
|
77
|
+
* @param h
|
|
78
|
+
*/
|
|
79
|
+
public PointQuadtree(float x, float y, float w, float h) {
|
|
80
|
+
this(null, x, y, w, h);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
*
|
|
85
|
+
* @param parent
|
|
86
|
+
* @param x
|
|
87
|
+
* @param y
|
|
88
|
+
* @param w
|
|
89
|
+
* @param h
|
|
90
|
+
*/
|
|
91
|
+
public PointQuadtree(PointQuadtree parent, float x, float y, float w,
|
|
92
|
+
float h) {
|
|
93
|
+
super(x, y, w, h);
|
|
94
|
+
this.parent = parent;
|
|
95
|
+
this.type = Type.EMPTY;
|
|
96
|
+
mx = x + w * 0.5f;
|
|
97
|
+
my = y + h * 0.5f;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
*
|
|
102
|
+
* @param r
|
|
103
|
+
*/
|
|
104
|
+
public PointQuadtree(Rect r) {
|
|
105
|
+
this(null, r.x, r.y, r.width, r.height);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private void balance() {
|
|
109
|
+
switch (type) {
|
|
110
|
+
case EMPTY:
|
|
111
|
+
case LEAF:
|
|
112
|
+
if (parent != null) {
|
|
113
|
+
parent.balance();
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
|
|
117
|
+
case BRANCH:
|
|
118
|
+
PointQuadtree leaf = null;
|
|
119
|
+
if (childNW.type != Type.EMPTY) {
|
|
120
|
+
leaf = childNW;
|
|
121
|
+
}
|
|
122
|
+
if (childNE.type != Type.EMPTY) {
|
|
123
|
+
if (leaf != null) {
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
leaf = childNE;
|
|
127
|
+
}
|
|
128
|
+
if (childSW.type != Type.EMPTY) {
|
|
129
|
+
if (leaf != null) {
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
leaf = childSW;
|
|
133
|
+
}
|
|
134
|
+
if (childSE.type != Type.EMPTY) {
|
|
135
|
+
if (leaf != null) {
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
leaf = childSE;
|
|
139
|
+
}
|
|
140
|
+
if (leaf == null) {
|
|
141
|
+
type = Type.EMPTY;
|
|
142
|
+
childNW = childNE = childSW = childSE = null;
|
|
143
|
+
} else if (leaf.type == Type.BRANCH) {
|
|
144
|
+
break;
|
|
145
|
+
} else {
|
|
146
|
+
type = Type.LEAF;
|
|
147
|
+
childNW = childNE = childSW = childSE = null;
|
|
148
|
+
value = leaf.value;
|
|
149
|
+
}
|
|
150
|
+
if (parent != null) {
|
|
151
|
+
parent.balance();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
*
|
|
158
|
+
*/
|
|
159
|
+
@Override
|
|
160
|
+
public void clear() {
|
|
161
|
+
childNW = childNE = childSW = childSE = null;
|
|
162
|
+
type = Type.EMPTY;
|
|
163
|
+
value = null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
*
|
|
168
|
+
* @param p
|
|
169
|
+
* @return
|
|
170
|
+
*/
|
|
171
|
+
public PointQuadtree findNode(Vec2D p) {
|
|
172
|
+
switch (type) {
|
|
173
|
+
case EMPTY:
|
|
174
|
+
return null;
|
|
175
|
+
case LEAF:
|
|
176
|
+
return value.x == x && value.y == y ? this : null;
|
|
177
|
+
case BRANCH:
|
|
178
|
+
return getQuadrantForPoint(p.x, p.y).findNode(p);
|
|
179
|
+
default:
|
|
180
|
+
throw new IllegalStateException("Invalid node type");
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
private PointQuadtree getQuadrantForPoint(float x, float y) {
|
|
185
|
+
if (x < mx) {
|
|
186
|
+
return y < my ? childNW : childSW;
|
|
187
|
+
} else {
|
|
188
|
+
return y < my ? childNE : childSE;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
*
|
|
194
|
+
* @param p
|
|
195
|
+
* @return
|
|
196
|
+
*/
|
|
197
|
+
@Override
|
|
198
|
+
public boolean index(Vec2D p) {
|
|
199
|
+
if (containsPoint(p)) {
|
|
200
|
+
switch (type) {
|
|
201
|
+
case EMPTY:
|
|
202
|
+
setPoint(p);
|
|
203
|
+
return true;
|
|
204
|
+
|
|
205
|
+
case LEAF:
|
|
206
|
+
if (value.x == p.x && value.y == p.y) {
|
|
207
|
+
return false;
|
|
208
|
+
} else {
|
|
209
|
+
split();
|
|
210
|
+
return getQuadrantForPoint(p.x, p.y).index(p);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
case BRANCH:
|
|
214
|
+
return getQuadrantForPoint(p.x, p.y).index(p);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
*
|
|
222
|
+
* @param p
|
|
223
|
+
* @return
|
|
224
|
+
*/
|
|
225
|
+
@Override
|
|
226
|
+
public boolean isIndexed(Vec2D p) {
|
|
227
|
+
return findNode(p) != null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
*
|
|
232
|
+
* @param p
|
|
233
|
+
* @param radius
|
|
234
|
+
* @param results
|
|
235
|
+
* @return
|
|
236
|
+
*/
|
|
237
|
+
@Override
|
|
238
|
+
public List<Vec2D> itemsWithinRadius(Vec2D p, float radius,
|
|
239
|
+
List<Vec2D> results) {
|
|
240
|
+
if (intersectsCircle(p, radius)) {
|
|
241
|
+
if (type == Type.LEAF) {
|
|
242
|
+
if (value.distanceToSquared(p) < radius * radius) {
|
|
243
|
+
if (results == null) {
|
|
244
|
+
results = new ArrayList<>();
|
|
245
|
+
}
|
|
246
|
+
results.add(value);
|
|
247
|
+
}
|
|
248
|
+
} else if (type == Type.BRANCH) {
|
|
249
|
+
PointQuadtree[] children = new PointQuadtree[] {
|
|
250
|
+
childNW, childNE, childSW, childSE
|
|
251
|
+
};
|
|
252
|
+
for (int i = 0; i < 4; i++) {
|
|
253
|
+
if (children[i] != null) {
|
|
254
|
+
results = children[i].itemsWithinRadius(p, radius,
|
|
255
|
+
results);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return results;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
*
|
|
265
|
+
* @param bounds
|
|
266
|
+
* @param results
|
|
267
|
+
* @return
|
|
268
|
+
*/
|
|
269
|
+
public List<Vec2D> itemsWithinRect(Rect bounds, List<Vec2D> results) {
|
|
270
|
+
if (bounds.intersectsRect(this)) {
|
|
271
|
+
if (type == Type.LEAF) {
|
|
272
|
+
if (bounds.containsPoint(value)) {
|
|
273
|
+
if (results == null) {
|
|
274
|
+
results = new ArrayList<>();
|
|
275
|
+
}
|
|
276
|
+
results.add(value);
|
|
277
|
+
}
|
|
278
|
+
} else if (type == Type.BRANCH) {
|
|
279
|
+
PointQuadtree[] children = new PointQuadtree[] {
|
|
280
|
+
childNW, childNE, childSW, childSE
|
|
281
|
+
};
|
|
282
|
+
for (int i = 0; i < 4; i++) {
|
|
283
|
+
if (children[i] != null) {
|
|
284
|
+
results = children[i].itemsWithinRect(bounds, results);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return results;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
*
|
|
294
|
+
* @param visitor
|
|
295
|
+
*/
|
|
296
|
+
public void prewalk(QuadtreeVisitor visitor) {
|
|
297
|
+
switch (type) {
|
|
298
|
+
case LEAF:
|
|
299
|
+
visitor.visitNode(this);
|
|
300
|
+
break;
|
|
301
|
+
|
|
302
|
+
case BRANCH:
|
|
303
|
+
visitor.visitNode(this);
|
|
304
|
+
childNW.prewalk(visitor);
|
|
305
|
+
childNE.prewalk(visitor);
|
|
306
|
+
childSW.prewalk(visitor);
|
|
307
|
+
childSE.prewalk(visitor);
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
*
|
|
314
|
+
* @param p
|
|
315
|
+
* @param q
|
|
316
|
+
* @return
|
|
317
|
+
*/
|
|
318
|
+
@Override
|
|
319
|
+
public boolean reindex(Vec2D p, Vec2D q) {
|
|
320
|
+
unindex(p);
|
|
321
|
+
return index(q);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private void setPoint(Vec2D p) {
|
|
325
|
+
if (type == Type.BRANCH) {
|
|
326
|
+
throw new IllegalStateException("invalid node type: BRANCH");
|
|
327
|
+
}
|
|
328
|
+
type = Type.LEAF;
|
|
329
|
+
value = p;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
*
|
|
334
|
+
* @return
|
|
335
|
+
*/
|
|
336
|
+
@Override
|
|
337
|
+
public int size() {
|
|
338
|
+
return 0;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private void split() {
|
|
342
|
+
Vec2D oldPoint = value;
|
|
343
|
+
value = null;
|
|
344
|
+
|
|
345
|
+
type = Type.BRANCH;
|
|
346
|
+
|
|
347
|
+
float w2 = width * 0.5f;
|
|
348
|
+
float h2 = height * 0.5f;
|
|
349
|
+
|
|
350
|
+
childNW = new PointQuadtree(this, x, y, w2, h2);
|
|
351
|
+
childNE = new PointQuadtree(this, x + w2, y, w2, h2);
|
|
352
|
+
childSW = new PointQuadtree(this, x, y + h2, w2, h2);
|
|
353
|
+
childSE = new PointQuadtree(this, x + w2, y + h2, w2, h2);
|
|
354
|
+
|
|
355
|
+
index(oldPoint);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
*
|
|
360
|
+
* @param p
|
|
361
|
+
* @return
|
|
362
|
+
*/
|
|
363
|
+
@Override
|
|
364
|
+
public boolean unindex(Vec2D p) {
|
|
365
|
+
PointQuadtree node = findNode(p);
|
|
366
|
+
if (node != null) {
|
|
367
|
+
node.value = null;
|
|
368
|
+
node.type = Type.EMPTY;
|
|
369
|
+
node.balance();
|
|
370
|
+
return true;
|
|
371
|
+
} else {
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
@@ -0,0 +1,1038 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* __ .__ .__ ._____.
|
|
3
|
+
* _/ |_ _______ __|__| ____ | | |__\_ |__ ______
|
|
4
|
+
* \ __\/ _ \ \/ / |/ ___\| | | || __ \ / ___/
|
|
5
|
+
* | | ( <_> > <| \ \___| |_| || \_\ \\___ \
|
|
6
|
+
* |__| \____/__/\_ \__|\___ >____/__||___ /____ >
|
|
7
|
+
* \/ \/ \/ \/
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2006-2011 Karsten Schmidt
|
|
10
|
+
*
|
|
11
|
+
* This library is free software; you can redistribute it and/or
|
|
12
|
+
* modify it under the terms of the GNU Lesser General Public
|
|
13
|
+
* License as published by the Free Software Foundation; either
|
|
14
|
+
* version 2.1 of the License, or (at your option) any later version.
|
|
15
|
+
*
|
|
16
|
+
* http://creativecommons.org/licenses/LGPL/2.1/
|
|
17
|
+
*
|
|
18
|
+
* This library is distributed in the hope that it will be useful,
|
|
19
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
21
|
+
* Lesser General Public License for more details.
|
|
22
|
+
*
|
|
23
|
+
* You should have received a copy of the GNU Lesser General Public
|
|
24
|
+
* License along with this library; if not, write to the Free Software
|
|
25
|
+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
package toxi.geom;
|
|
29
|
+
|
|
30
|
+
import java.util.ArrayList;
|
|
31
|
+
import java.util.Collections;
|
|
32
|
+
import java.util.Iterator;
|
|
33
|
+
import java.util.List;
|
|
34
|
+
|
|
35
|
+
import javax.xml.bind.annotation.XmlElement;
|
|
36
|
+
|
|
37
|
+
import toxi.geom.Line2D.LineIntersection;
|
|
38
|
+
import toxi.geom.Line2D.LineIntersection.Type;
|
|
39
|
+
import toxi.geom.mesh.Mesh3D;
|
|
40
|
+
import toxi.geom.mesh.TriangleMesh;
|
|
41
|
+
import toxi.math.MathUtils;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Container type for convex polygons. Implements {@link Shape2D}.
|
|
45
|
+
*/
|
|
46
|
+
public class Polygon2D implements Shape2D, Iterable<Vec2D> {
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Constructs a new regular polygon from the given base line/edge.
|
|
50
|
+
*
|
|
51
|
+
* @param baseA
|
|
52
|
+
* left point of the base edge
|
|
53
|
+
* @param baseB
|
|
54
|
+
* right point of the base edge
|
|
55
|
+
* @param res
|
|
56
|
+
* number of polygon vertices
|
|
57
|
+
* @return polygon
|
|
58
|
+
*/
|
|
59
|
+
public static Polygon2D fromBaseEdge(Vec2D baseA, Vec2D baseB, int res) {
|
|
60
|
+
float theta = -(MathUtils.PI - (MathUtils.PI * (res - 2) / res));
|
|
61
|
+
Vec2D dir = baseB.sub(baseA);
|
|
62
|
+
Vec2D prev = baseB;
|
|
63
|
+
Polygon2D poly = new Polygon2D(baseA, baseB);
|
|
64
|
+
for (int i = 1; i < res - 1; i++) {
|
|
65
|
+
Vec2D p = prev.add(dir.getRotated(theta * i));
|
|
66
|
+
poly.add(p);
|
|
67
|
+
prev = p;
|
|
68
|
+
}
|
|
69
|
+
return poly;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Constructs a regular polygon from the given edge length and number of
|
|
74
|
+
* vertices. This automatically computes the radius of the circle the
|
|
75
|
+
* polygon is inscribed in.
|
|
76
|
+
*
|
|
77
|
+
* <p>
|
|
78
|
+
* More information: http://en.wikipedia.org/wiki/Regular_polygon#Radius
|
|
79
|
+
* </p>
|
|
80
|
+
*
|
|
81
|
+
* @param len
|
|
82
|
+
* desired edge length
|
|
83
|
+
* @param res
|
|
84
|
+
* number of vertices
|
|
85
|
+
* @return polygon
|
|
86
|
+
*/
|
|
87
|
+
public static Polygon2D fromEdgeLength(float len, int res) {
|
|
88
|
+
return new Circle(getRadiusForEdgeLength(len, res)).toPolygon2D(res);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Computes the radius of the circle the regular polygon with the desired
|
|
93
|
+
* edge length is inscribed in
|
|
94
|
+
*
|
|
95
|
+
* @param len
|
|
96
|
+
* edge length
|
|
97
|
+
* @param res
|
|
98
|
+
* number of polygon vertices
|
|
99
|
+
* @return radius
|
|
100
|
+
*/
|
|
101
|
+
public static float getRadiusForEdgeLength(float len, int res) {
|
|
102
|
+
return len / (2 * MathUtils.sin(MathUtils.PI / res));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
*
|
|
107
|
+
*/
|
|
108
|
+
@XmlElement(name = "v")
|
|
109
|
+
public List<Vec2D> vertices = new ArrayList<Vec2D>();
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
*
|
|
113
|
+
*/
|
|
114
|
+
public Polygon2D() {
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
*
|
|
119
|
+
* @param points
|
|
120
|
+
*/
|
|
121
|
+
public Polygon2D(List<Vec2D> points) {
|
|
122
|
+
for (Vec2D p : points) {
|
|
123
|
+
add(p.copy());
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
*
|
|
129
|
+
* @param points
|
|
130
|
+
*/
|
|
131
|
+
public Polygon2D(Vec2D... points) {
|
|
132
|
+
for (Vec2D p : points) {
|
|
133
|
+
add(p.copy());
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Adds a new vertex to the polygon (builder pattern).
|
|
139
|
+
*
|
|
140
|
+
* @param x
|
|
141
|
+
* @param y
|
|
142
|
+
* @return itself
|
|
143
|
+
*/
|
|
144
|
+
public Polygon2D add(float x, float y) {
|
|
145
|
+
return add(new Vec2D(x, y));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Adds a new vertex to the polygon (builder pattern).
|
|
150
|
+
*
|
|
151
|
+
* @param p
|
|
152
|
+
* vertex point to add
|
|
153
|
+
* @return itself
|
|
154
|
+
*/
|
|
155
|
+
public Polygon2D add(Vec2D p) {
|
|
156
|
+
if (!vertices.contains(p)) {
|
|
157
|
+
vertices.add(p);
|
|
158
|
+
}
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Centers the polygon around the world origin (0,0).
|
|
164
|
+
*
|
|
165
|
+
* @return itself
|
|
166
|
+
*/
|
|
167
|
+
public Polygon2D center() {
|
|
168
|
+
return center(null);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Centers the polygon so that its new centroid is at the given point.
|
|
173
|
+
*
|
|
174
|
+
* @param origin
|
|
175
|
+
* new centroid or null to center around (0,0)
|
|
176
|
+
* @return itself
|
|
177
|
+
*/
|
|
178
|
+
public Polygon2D center(ReadonlyVec2D origin) {
|
|
179
|
+
Vec2D centroid = getCentroid();
|
|
180
|
+
Vec2D delta = origin != null ? origin.sub(centroid) : centroid.invert();
|
|
181
|
+
for (Vec2D v : vertices) {
|
|
182
|
+
v.addSelf(delta);
|
|
183
|
+
}
|
|
184
|
+
return this;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
public boolean containsPoint(ReadonlyVec2D p) {
|
|
188
|
+
int num = vertices.size();
|
|
189
|
+
int i, j = num - 1;
|
|
190
|
+
boolean oddNodes = false;
|
|
191
|
+
float px = p.x();
|
|
192
|
+
float py = p.y();
|
|
193
|
+
for (i = 0; i < num; i++) {
|
|
194
|
+
Vec2D vi = vertices.get(i);
|
|
195
|
+
Vec2D vj = vertices.get(j);
|
|
196
|
+
if (vi.y < py && vj.y >= py || vj.y < py && vi.y >= py) {
|
|
197
|
+
if (vi.x + (py - vi.y) / (vj.y - vi.y) * (vj.x - vi.x) < px) {
|
|
198
|
+
oddNodes = !oddNodes;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
j = i;
|
|
202
|
+
}
|
|
203
|
+
return oddNodes;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
*
|
|
208
|
+
* @param poly
|
|
209
|
+
* @return
|
|
210
|
+
*/
|
|
211
|
+
public boolean containsPolygon(Polygon2D poly) {
|
|
212
|
+
for (Vec2D p : poly.vertices) {
|
|
213
|
+
if (!containsPoint(p)) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
*
|
|
222
|
+
* @return
|
|
223
|
+
*/
|
|
224
|
+
public Polygon2D copy() {
|
|
225
|
+
return new Polygon2D(vertices);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Flips the ordering of the polygon's vertices.
|
|
230
|
+
*
|
|
231
|
+
* @return itself
|
|
232
|
+
*/
|
|
233
|
+
public Polygon2D flipVertexOrder() {
|
|
234
|
+
Collections.reverse(vertices);
|
|
235
|
+
return this;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Returns the vertex at the given index. This function follows Python
|
|
240
|
+
* convention, in that if the index is negative, it is considered relative
|
|
241
|
+
* to the list end. Therefore the vertex at index -1 is the last vertex in
|
|
242
|
+
* the list.
|
|
243
|
+
*
|
|
244
|
+
* @param i
|
|
245
|
+
* index
|
|
246
|
+
* @return vertex
|
|
247
|
+
*/
|
|
248
|
+
public Vec2D get(int i) {
|
|
249
|
+
if (i < 0) {
|
|
250
|
+
i += vertices.size();
|
|
251
|
+
}
|
|
252
|
+
return vertices.get(i);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Computes the length of this polygon's apothem. This will only be valid if
|
|
257
|
+
* the polygon is regular. More info: http://en.wikipedia.org/wiki/Apothem
|
|
258
|
+
*
|
|
259
|
+
* @return apothem length
|
|
260
|
+
*/
|
|
261
|
+
public float getApothem() {
|
|
262
|
+
return vertices.get(0).interpolateTo(vertices.get(1), 0.5f)
|
|
263
|
+
.distanceTo(getCentroid());
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Computes the area of the polygon, provided it isn't self intersecting.
|
|
268
|
+
* Code ported from:
|
|
269
|
+
* http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
|
|
270
|
+
*
|
|
271
|
+
* @return polygon area
|
|
272
|
+
*/
|
|
273
|
+
public float getArea() {
|
|
274
|
+
float area = 0;
|
|
275
|
+
for (int i = 0, num = vertices.size(); i < num; i++) {
|
|
276
|
+
Vec2D a = vertices.get(i);
|
|
277
|
+
Vec2D b = vertices.get((i + 1) % num);
|
|
278
|
+
area += a.x * b.y;
|
|
279
|
+
area -= a.y * b.x;
|
|
280
|
+
}
|
|
281
|
+
area *= 0.5f;
|
|
282
|
+
return area;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
public Circle getBoundingCircle() {
|
|
286
|
+
return Circle.newBoundingCircle(vertices);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Returns the polygon's bounding rect.
|
|
291
|
+
*
|
|
292
|
+
* @return bounding rect
|
|
293
|
+
* @see toxi.geom.Shape2D#getBounds()
|
|
294
|
+
*/
|
|
295
|
+
public Rect getBounds() {
|
|
296
|
+
return Rect.getBoundingRect(vertices);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Computes the polygon's centre of mass. Code ported from:
|
|
301
|
+
* http://local.wasp.uwa.edu.au/~pbourke/geometry/polyarea/
|
|
302
|
+
*
|
|
303
|
+
* @return centroid point
|
|
304
|
+
*/
|
|
305
|
+
public Vec2D getCentroid() {
|
|
306
|
+
Vec2D res = new Vec2D();
|
|
307
|
+
for (int i = 0, num = vertices.size(); i < num; i++) {
|
|
308
|
+
Vec2D a = vertices.get(i);
|
|
309
|
+
Vec2D b = vertices.get((i + 1) % num);
|
|
310
|
+
float crossP = a.x * b.y - b.x * a.y;
|
|
311
|
+
res.x += (a.x + b.x) * crossP;
|
|
312
|
+
res.y += (a.y + b.y) * crossP;
|
|
313
|
+
}
|
|
314
|
+
return res.scale(1f / (6 * getArea()));
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Computes the polygon's circumference, the length of its perimeter.
|
|
319
|
+
*
|
|
320
|
+
* @return perimiter length
|
|
321
|
+
*
|
|
322
|
+
* @see toxi.geom.Shape2D#getCircumference()
|
|
323
|
+
*/
|
|
324
|
+
public float getCircumference() {
|
|
325
|
+
float circ = 0;
|
|
326
|
+
for (int i = 0, num = vertices.size(); i < num; i++) {
|
|
327
|
+
circ += vertices.get(i).distanceTo(vertices.get((i + 1) % num));
|
|
328
|
+
}
|
|
329
|
+
return circ;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
*
|
|
334
|
+
* @param p
|
|
335
|
+
* @return
|
|
336
|
+
*/
|
|
337
|
+
public Vec2D getClosestPointTo(ReadonlyVec2D p) {
|
|
338
|
+
float minD = Float.MAX_VALUE;
|
|
339
|
+
Vec2D q = null;
|
|
340
|
+
for (Line2D l : getEdges()) {
|
|
341
|
+
Vec2D c = l.closestPointTo(p);
|
|
342
|
+
float d = c.distanceToSquared(p);
|
|
343
|
+
if (d < minD) {
|
|
344
|
+
q = c;
|
|
345
|
+
minD = d;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return q;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
*
|
|
353
|
+
* @param p
|
|
354
|
+
* @return
|
|
355
|
+
*/
|
|
356
|
+
public Vec2D getClosestVertexTo(ReadonlyVec2D p) {
|
|
357
|
+
float minD = Float.MAX_VALUE;
|
|
358
|
+
Vec2D q = null;
|
|
359
|
+
for (Vec2D v : vertices) {
|
|
360
|
+
float d = v.distanceToSquared(p);
|
|
361
|
+
if (d < minD) {
|
|
362
|
+
q = v;
|
|
363
|
+
minD = d;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return q;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Returns a list of {@link Line2D} segments representing the polygon edges.
|
|
371
|
+
*
|
|
372
|
+
* @return list of lines
|
|
373
|
+
*/
|
|
374
|
+
public List<Line2D> getEdges() {
|
|
375
|
+
int num = vertices.size();
|
|
376
|
+
List<Line2D> edges = new ArrayList<Line2D>(num);
|
|
377
|
+
for (int i = 0; i < num; i++) {
|
|
378
|
+
edges.add(new Line2D(vertices.get(i), vertices.get((i + 1) % num)));
|
|
379
|
+
}
|
|
380
|
+
return edges;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* @return
|
|
385
|
+
* @see #getNumVertices()
|
|
386
|
+
*/
|
|
387
|
+
@Deprecated
|
|
388
|
+
public int getNumPoints() {
|
|
389
|
+
return getNumVertices();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Returns the number of polygon vertices.
|
|
394
|
+
*
|
|
395
|
+
* @return vertex count
|
|
396
|
+
*/
|
|
397
|
+
public int getNumVertices() {
|
|
398
|
+
return vertices.size();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Creates a random point within the polygon. This is only guaranteed to
|
|
403
|
+
* work with regular polygons.
|
|
404
|
+
*
|
|
405
|
+
* @return Vec2D
|
|
406
|
+
*/
|
|
407
|
+
public Vec2D getRandomPoint() {
|
|
408
|
+
List<Line2D> edges = getEdges();
|
|
409
|
+
int numEdges = edges.size();
|
|
410
|
+
Line2D ea = edges.get(MathUtils.random(numEdges));
|
|
411
|
+
Line2D eb = null;
|
|
412
|
+
// and another one, making sure it's different
|
|
413
|
+
while (eb == null || eb.equals(ea)) {
|
|
414
|
+
eb = edges.get(MathUtils.random(numEdges));
|
|
415
|
+
}
|
|
416
|
+
// pick a random point on edge A
|
|
417
|
+
Vec2D p = ea.a.interpolateTo(ea.b, MathUtils.random(1f));
|
|
418
|
+
// then randomly interpolate to another random point on edge B
|
|
419
|
+
return p.interpolateToSelf(
|
|
420
|
+
eb.a.interpolateTo(eb.b, MathUtils.random(1f)),
|
|
421
|
+
MathUtils.random(1f));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Repeatedly inserts vertices as mid points of the longest edges until the
|
|
426
|
+
* new vertex count is reached.
|
|
427
|
+
*
|
|
428
|
+
* @param count
|
|
429
|
+
* new vertex count
|
|
430
|
+
* @return itself
|
|
431
|
+
*/
|
|
432
|
+
public Polygon2D increaseVertexCount(int count) {
|
|
433
|
+
int num = vertices.size();
|
|
434
|
+
while (num < count) {
|
|
435
|
+
// find longest edge
|
|
436
|
+
int longestID = 0;
|
|
437
|
+
float maxD = 0;
|
|
438
|
+
for (int i = 0; i < num; i++) {
|
|
439
|
+
float d = vertices.get(i).distanceToSquared(
|
|
440
|
+
vertices.get((i + 1) % num));
|
|
441
|
+
if (d > maxD) {
|
|
442
|
+
longestID = i;
|
|
443
|
+
maxD = d;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
// insert mid point of longest segment in vertex list
|
|
447
|
+
Vec2D m = vertices.get(longestID)
|
|
448
|
+
.add(vertices.get((longestID + 1) % num)).scaleSelf(0.5f);
|
|
449
|
+
vertices.add(longestID + 1, m);
|
|
450
|
+
num++;
|
|
451
|
+
}
|
|
452
|
+
return this;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
*
|
|
457
|
+
* @param l
|
|
458
|
+
* @param edges
|
|
459
|
+
* @return
|
|
460
|
+
*/
|
|
461
|
+
protected boolean intersectsLine(Line2D l, List<Line2D> edges) {
|
|
462
|
+
for (Line2D e : edges) {
|
|
463
|
+
final Type isec = l.intersectLine(e).getType();
|
|
464
|
+
if (isec == Type.INTERSECTING || isec == Type.COINCIDENT) {
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Checks if the given polygon intersect this one by checking all edges for
|
|
473
|
+
* line intersections.
|
|
474
|
+
*
|
|
475
|
+
* @param poly
|
|
476
|
+
* @return true, if polygons intersect.
|
|
477
|
+
*/
|
|
478
|
+
public boolean intersectsPolygon(Polygon2D poly) {
|
|
479
|
+
List<Line2D> edgesB = poly.getEdges();
|
|
480
|
+
for (Line2D ea : getEdges()) {
|
|
481
|
+
if (intersectsLine(ea, edgesB)) {
|
|
482
|
+
return true;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
*
|
|
490
|
+
* @param r
|
|
491
|
+
* @return
|
|
492
|
+
*/
|
|
493
|
+
public boolean intersectsRect(Rect r) {
|
|
494
|
+
List<Line2D> edges = r.getEdges();
|
|
495
|
+
for (Line2D ea : getEdges()) {
|
|
496
|
+
if (intersectsLine(ea, edges)) {
|
|
497
|
+
return true;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Checks if the vertices of this polygon are in clockwise ordering by
|
|
505
|
+
* examining all vertices as a sequence of triangles. The test relies on the
|
|
506
|
+
* fact that the {@link #getArea()} method will produce negative results for
|
|
507
|
+
* ant-clockwise ordered polygons.
|
|
508
|
+
*
|
|
509
|
+
* @return true, if clockwise
|
|
510
|
+
*/
|
|
511
|
+
public boolean isClockwise() {
|
|
512
|
+
return getArea() > 0;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Checks if the polygon is convex.
|
|
517
|
+
*
|
|
518
|
+
* @return true, if convex.
|
|
519
|
+
*/
|
|
520
|
+
public boolean isConvex() {
|
|
521
|
+
boolean isPositive = false;
|
|
522
|
+
int num = vertices.size();
|
|
523
|
+
for (int i = 0; i < num; i++) {
|
|
524
|
+
int prev = (i == 0) ? num - 1 : i - 1;
|
|
525
|
+
int next = (i == num - 1) ? 0 : i + 1;
|
|
526
|
+
Vec2D d0 = vertices.get(i).sub(vertices.get(prev));
|
|
527
|
+
Vec2D d1 = vertices.get(next).sub(vertices.get(i));
|
|
528
|
+
boolean newIsP = (d0.cross(d1) > 0);
|
|
529
|
+
if (i == 0) {
|
|
530
|
+
isPositive = newIsP;
|
|
531
|
+
} else if (isPositive != newIsP) {
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return true;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
*
|
|
540
|
+
* @return
|
|
541
|
+
*/
|
|
542
|
+
public Iterator<Vec2D> iterator() {
|
|
543
|
+
return vertices.iterator();
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Given the sequentially connected points p1, p2, p3, this function returns
|
|
548
|
+
* a bevel-offset replacement for point p2.
|
|
549
|
+
*
|
|
550
|
+
* Note: If vectors p1->p2 and p2->p3 are exactly 180 degrees opposed, or if
|
|
551
|
+
* either segment is zero then no offset will be applied.
|
|
552
|
+
*
|
|
553
|
+
* @param x1
|
|
554
|
+
* @param y1
|
|
555
|
+
* @param x2
|
|
556
|
+
* @param y2
|
|
557
|
+
* @param x3
|
|
558
|
+
* @param y3
|
|
559
|
+
* @param distance
|
|
560
|
+
* @param out
|
|
561
|
+
*
|
|
562
|
+
* @see http://alienryderflex.com/polygon_inset/
|
|
563
|
+
*/
|
|
564
|
+
protected void offsetCorner(float x1, float y1, float x2, float y2,
|
|
565
|
+
float x3, float y3, float distance, Vec2D out) {
|
|
566
|
+
|
|
567
|
+
float c1 = x2, d1 = y2, c2 = x2, d2 = y2;
|
|
568
|
+
float dx1, dy1, dist1, dx2, dy2, dist2, insetX, insetY;
|
|
569
|
+
|
|
570
|
+
dx1 = x2 - x1;
|
|
571
|
+
dy1 = y2 - y1;
|
|
572
|
+
dist1 = (float) Math.sqrt(dx1 * dx1 + dy1 * dy1);
|
|
573
|
+
dx2 = x3 - x2;
|
|
574
|
+
dy2 = y3 - y2;
|
|
575
|
+
dist2 = (float) Math.sqrt(dx2 * dx2 + dy2 * dy2);
|
|
576
|
+
|
|
577
|
+
if (dist1 < MathUtils.EPS || dist2 < MathUtils.EPS) {
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
dist1 = distance / dist1;
|
|
581
|
+
dist2 = distance / dist2;
|
|
582
|
+
|
|
583
|
+
insetX = dy1 * dist1;
|
|
584
|
+
insetY = -dx1 * dist1;
|
|
585
|
+
x1 += insetX;
|
|
586
|
+
c1 += insetX;
|
|
587
|
+
y1 += insetY;
|
|
588
|
+
d1 += insetY;
|
|
589
|
+
insetX = dy2 * dist2;
|
|
590
|
+
insetY = -dx2 * dist2;
|
|
591
|
+
x3 += insetX;
|
|
592
|
+
c2 += insetX;
|
|
593
|
+
y3 += insetY;
|
|
594
|
+
d2 += insetY;
|
|
595
|
+
|
|
596
|
+
if (c1 == c2 && d1 == d2) {
|
|
597
|
+
out.set(c1, d1);
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
Line2D l1 = new Line2D(new Vec2D(x1, y1), new Vec2D(c1, d1));
|
|
602
|
+
Line2D l2 = new Line2D(new Vec2D(c2, d2), new Vec2D(x3, y3));
|
|
603
|
+
LineIntersection isec = l1.intersectLine(l2);
|
|
604
|
+
final Vec2D ipos = isec.getPos();
|
|
605
|
+
if (ipos != null) {
|
|
606
|
+
out.set(ipos);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Moves each line segment of the polygon in/outward perpendicular by the
|
|
612
|
+
* given distance. New line segments and polygon vertices are created by
|
|
613
|
+
* computing the intersection points of the displaced segments. Choosing an
|
|
614
|
+
* too large displacement amount will result in deformation/undefined
|
|
615
|
+
* behavior with various self intersections. Should that happen, please try
|
|
616
|
+
* to clean up the shape using the {@link #toOutline()} method.
|
|
617
|
+
*
|
|
618
|
+
* @param distance
|
|
619
|
+
* offset/inset distance (negative for inset)
|
|
620
|
+
* @return itself
|
|
621
|
+
*/
|
|
622
|
+
public Polygon2D offsetShape(float distance) {
|
|
623
|
+
int num = vertices.size() - 1;
|
|
624
|
+
if (num > 1) {
|
|
625
|
+
float startX = vertices.get(0).x;
|
|
626
|
+
float startY = vertices.get(0).y;
|
|
627
|
+
float c = vertices.get(num).x;
|
|
628
|
+
float d = vertices.get(num).y;
|
|
629
|
+
float e = startX;
|
|
630
|
+
float f = startY;
|
|
631
|
+
for (int i = 0; i < num; i++) {
|
|
632
|
+
float a = c;
|
|
633
|
+
float b = d;
|
|
634
|
+
c = e;
|
|
635
|
+
d = f;
|
|
636
|
+
e = vertices.get(i + 1).x;
|
|
637
|
+
f = vertices.get(i + 1).y;
|
|
638
|
+
offsetCorner(a, b, c, d, e, f, distance, vertices.get(i));
|
|
639
|
+
}
|
|
640
|
+
offsetCorner(c, d, e, f, startX, startY, distance,
|
|
641
|
+
vertices.get(num));
|
|
642
|
+
}
|
|
643
|
+
return this;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Reduces the number of vertices in the polygon based on the given minimum
|
|
648
|
+
* edge length. Only vertices with at least this distance between them will
|
|
649
|
+
* be kept.
|
|
650
|
+
*
|
|
651
|
+
* @param minEdgeLen
|
|
652
|
+
* @return itself
|
|
653
|
+
*/
|
|
654
|
+
public Polygon2D reduceVertices(float minEdgeLen) {
|
|
655
|
+
minEdgeLen *= minEdgeLen;
|
|
656
|
+
List<Vec2D> reduced = new ArrayList<Vec2D>();
|
|
657
|
+
Vec2D prev = vertices.get(0);
|
|
658
|
+
reduced.add(prev);
|
|
659
|
+
int num = vertices.size() - 1;
|
|
660
|
+
for (int i = 1; i < num; i++) {
|
|
661
|
+
Vec2D v = vertices.get(i);
|
|
662
|
+
if (prev.distanceToSquared(v) >= minEdgeLen) {
|
|
663
|
+
reduced.add(v);
|
|
664
|
+
prev = v;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
if (vertices.get(0).distanceToSquared(vertices.get(num)) >= minEdgeLen) {
|
|
668
|
+
reduced.add(vertices.get(num));
|
|
669
|
+
}
|
|
670
|
+
vertices = reduced;
|
|
671
|
+
return this;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Removes duplicate vertices from the polygon. Only successive points are
|
|
676
|
+
* recognized as duplicates.
|
|
677
|
+
*
|
|
678
|
+
* @param tolerance
|
|
679
|
+
* snap distance for finding duplicates
|
|
680
|
+
* @return itself
|
|
681
|
+
*/
|
|
682
|
+
public Polygon2D removeDuplicates(float tolerance) {
|
|
683
|
+
Vec2D prev = null;
|
|
684
|
+
for (Iterator<Vec2D> iv = vertices.iterator(); iv.hasNext();) {
|
|
685
|
+
Vec2D p = iv.next();
|
|
686
|
+
if (p.equalsWithTolerance(prev, tolerance)) {
|
|
687
|
+
iv.remove();
|
|
688
|
+
} else {
|
|
689
|
+
prev = p;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
int num = vertices.size();
|
|
693
|
+
if (num > 0) {
|
|
694
|
+
Vec2D last = vertices.get(num - 1);
|
|
695
|
+
if (last.equalsWithTolerance(vertices.get(0), tolerance)) {
|
|
696
|
+
vertices.remove(last);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return this;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
*
|
|
704
|
+
* @param theta
|
|
705
|
+
* @return
|
|
706
|
+
*/
|
|
707
|
+
public Polygon2D rotate(float theta) {
|
|
708
|
+
for (Vec2D v : vertices) {
|
|
709
|
+
v.rotate(theta);
|
|
710
|
+
}
|
|
711
|
+
return this;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
*
|
|
716
|
+
* @param scale
|
|
717
|
+
* @return
|
|
718
|
+
*/
|
|
719
|
+
public Polygon2D scale(float scale) {
|
|
720
|
+
return scale(scale, scale);
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
*
|
|
725
|
+
* @param x
|
|
726
|
+
* @param y
|
|
727
|
+
* @return
|
|
728
|
+
*/
|
|
729
|
+
public Polygon2D scale(float x, float y) {
|
|
730
|
+
for (Vec2D v : vertices) {
|
|
731
|
+
v.scaleSelf(x, y);
|
|
732
|
+
}
|
|
733
|
+
return this;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
*
|
|
738
|
+
* @param scale
|
|
739
|
+
* @return
|
|
740
|
+
*/
|
|
741
|
+
public Polygon2D scale(ReadonlyVec2D scale) {
|
|
742
|
+
return scale(scale.x(), scale.y());
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
*
|
|
747
|
+
* @param scale
|
|
748
|
+
* @return
|
|
749
|
+
*/
|
|
750
|
+
public Polygon2D scaleSize(float scale) {
|
|
751
|
+
return scaleSize(scale, scale);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
*
|
|
756
|
+
* @param x
|
|
757
|
+
* @param y
|
|
758
|
+
* @return
|
|
759
|
+
*/
|
|
760
|
+
public Polygon2D scaleSize(float x, float y) {
|
|
761
|
+
Vec2D centroid = getCentroid();
|
|
762
|
+
for (Vec2D v : vertices) {
|
|
763
|
+
v.subSelf(centroid).scaleSelf(x, y).addSelf(centroid);
|
|
764
|
+
}
|
|
765
|
+
return this;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
*
|
|
770
|
+
* @param scale
|
|
771
|
+
* @return
|
|
772
|
+
*/
|
|
773
|
+
public Polygon2D scaleSize(ReadonlyVec2D scale) {
|
|
774
|
+
return scaleSize(scale.x(), scale.y());
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/**
|
|
778
|
+
* Applies a laplacian-style smooth operation to all polygon vertices,
|
|
779
|
+
* causing sharp corners/angles to widen and results in a general smoother
|
|
780
|
+
* shape. Let the current vertex be A and its neighbours P and Q, then A
|
|
781
|
+
* will be moved by a specified amount into the direction given by
|
|
782
|
+
* (P-A)+(Q-A). Additionally, and to avoid shrinking of the shape through
|
|
783
|
+
* repeated iteration of this procedure, the vector A - C (Polygon centroid)
|
|
784
|
+
* is added as counter force and a weight for its impact can be specified.
|
|
785
|
+
* To keep the average size of the polygon stable, this weight value should
|
|
786
|
+
* be ~1/2 of the smooth amount.
|
|
787
|
+
*
|
|
788
|
+
* @param amount
|
|
789
|
+
* smooth amount (between 0 < x < 0.5)
|
|
790
|
+
* @param baseWeight
|
|
791
|
+
* counter weight (0 <= x < 1/2 * smooth amount)
|
|
792
|
+
* @return itself
|
|
793
|
+
*/
|
|
794
|
+
public Polygon2D smooth(float amount, float baseWeight) {
|
|
795
|
+
Vec2D centroid = getCentroid();
|
|
796
|
+
int num = vertices.size();
|
|
797
|
+
List<Vec2D> filtered = new ArrayList<Vec2D>(num);
|
|
798
|
+
for (int i = 0, j = num - 1, k = 1; i < num; i++) {
|
|
799
|
+
Vec2D a = vertices.get(i);
|
|
800
|
+
Vec2D dir = vertices.get(j).sub(a).addSelf(vertices.get(k).sub(a))
|
|
801
|
+
.addSelf(a.sub(centroid).scaleSelf(baseWeight));
|
|
802
|
+
filtered.add(a.add(dir.scaleSelf(amount)));
|
|
803
|
+
j++;
|
|
804
|
+
if (j == num) {
|
|
805
|
+
j = 0;
|
|
806
|
+
}
|
|
807
|
+
k++;
|
|
808
|
+
if (k == num) {
|
|
809
|
+
k = 0;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
vertices.clear();
|
|
813
|
+
vertices.addAll(filtered);
|
|
814
|
+
return this;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
*
|
|
819
|
+
* @param mesh
|
|
820
|
+
* @return
|
|
821
|
+
*/
|
|
822
|
+
public Mesh3D toMesh(Mesh3D mesh) {
|
|
823
|
+
return toMesh(mesh, null, 0);
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
*
|
|
828
|
+
* @param mesh
|
|
829
|
+
* @param centroid2D
|
|
830
|
+
* @param extrude
|
|
831
|
+
* @return
|
|
832
|
+
*/
|
|
833
|
+
public Mesh3D toMesh(Mesh3D mesh, Vec2D centroid2D, float extrude) {
|
|
834
|
+
if (mesh == null) {
|
|
835
|
+
mesh = new TriangleMesh();
|
|
836
|
+
}
|
|
837
|
+
final int num = vertices.size();
|
|
838
|
+
if (centroid2D == null) {
|
|
839
|
+
centroid2D = getCentroid();
|
|
840
|
+
}
|
|
841
|
+
Vec3D centroid = centroid2D.to3DXY();
|
|
842
|
+
centroid.z = extrude;
|
|
843
|
+
Rect bounds = getBounds();
|
|
844
|
+
Vec2D boundScale = new Vec2D(1f / bounds.width, 1f / bounds.height);
|
|
845
|
+
Vec2D uvC = centroid2D.sub(bounds.getTopLeft()).scaleSelf(boundScale);
|
|
846
|
+
for (int i = 1; i <= num; i++) {
|
|
847
|
+
Vec2D a = vertices.get(i % num);
|
|
848
|
+
Vec2D b = vertices.get(i - 1);
|
|
849
|
+
Vec2D uvA = a.sub(bounds.getTopLeft()).scaleSelf(boundScale);
|
|
850
|
+
Vec2D uvB = b.sub(bounds.getTopLeft()).scaleSelf(boundScale);
|
|
851
|
+
mesh.addFace(centroid, a.to3DXY(), b.to3DXY(), uvC, uvA, uvB);
|
|
852
|
+
}
|
|
853
|
+
return mesh;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* Attempts to remove all internal self-intersections and creates a new
|
|
858
|
+
* polygon only consisting of perimeter vertices. Ported from:
|
|
859
|
+
* http://alienryderflex.com/polygon_perimeter/
|
|
860
|
+
*
|
|
861
|
+
* @return true, if process completed succcessfully.
|
|
862
|
+
*/
|
|
863
|
+
public boolean toOutline() {
|
|
864
|
+
int corners = vertices.size();
|
|
865
|
+
int maxSegs = corners * 3;
|
|
866
|
+
List<Vec2D> newVerts = new ArrayList<Vec2D>(corners);
|
|
867
|
+
Vec2D[] segments = new Vec2D[maxSegs];
|
|
868
|
+
Vec2D[] segEnds = new Vec2D[maxSegs];
|
|
869
|
+
float[] segAngles = new float[maxSegs];
|
|
870
|
+
Vec2D start = vertices.get(0).copy();
|
|
871
|
+
float lastAngle = MathUtils.PI;
|
|
872
|
+
float a, b, c, d, e, f;
|
|
873
|
+
double angleDif, bestAngleDif;
|
|
874
|
+
int i, j = corners - 1, segs = 0;
|
|
875
|
+
|
|
876
|
+
if (corners > maxSegs) {
|
|
877
|
+
return false;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// 1,3. Reformulate the polygon as a set of line segments, and choose a
|
|
881
|
+
// starting point that must be on the perimeter.
|
|
882
|
+
for (i = 0; i < corners; i++) {
|
|
883
|
+
Vec2D pi = vertices.get(i);
|
|
884
|
+
Vec2D pj = vertices.get(j);
|
|
885
|
+
if (!pi.equals(pj)) {
|
|
886
|
+
segments[segs] = pi;
|
|
887
|
+
segEnds[segs++] = pj;
|
|
888
|
+
}
|
|
889
|
+
j = i;
|
|
890
|
+
if (pi.y > start.y || (pi.y == start.y && pi.x < start.x)) {
|
|
891
|
+
start.set(pi);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
if (segs == 0) {
|
|
895
|
+
return false;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// 2. Break the segments up at their intersection points.
|
|
899
|
+
for (i = 0; i < segs - 1; i++) {
|
|
900
|
+
for (j = i + 1; j < segs; j++) {
|
|
901
|
+
Line2D li = new Line2D(segments[i], segEnds[i]);
|
|
902
|
+
Line2D lj = new Line2D(segments[j], segEnds[j]);
|
|
903
|
+
LineIntersection isec = li.intersectLine(lj);
|
|
904
|
+
if (isec.getType() == Type.INTERSECTING) {
|
|
905
|
+
Vec2D ipos = isec.getPos();
|
|
906
|
+
if (!ipos.equals(segments[i]) && !ipos.equals(segEnds[i])) {
|
|
907
|
+
if (segs == maxSegs) {
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
910
|
+
segments[segs] = segments[i].copy();
|
|
911
|
+
segEnds[segs++] = ipos.copy();
|
|
912
|
+
segments[i] = ipos.copy();
|
|
913
|
+
}
|
|
914
|
+
if (!ipos.equals(segments[j]) && !ipos.equals(segEnds[j])) {
|
|
915
|
+
if (segs == maxSegs) {
|
|
916
|
+
return false;
|
|
917
|
+
}
|
|
918
|
+
segments[segs] = segments[j].copy();
|
|
919
|
+
segEnds[segs++] = ipos.copy();
|
|
920
|
+
segments[j] = ipos.copy();
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// Calculate the angle of each segment.
|
|
927
|
+
for (i = 0; i < segs; i++) {
|
|
928
|
+
segAngles[i] = segEnds[i].sub(segments[i]).positiveHeading();
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// 4. Build the perimeter polygon.
|
|
932
|
+
c = start.x;
|
|
933
|
+
d = start.y;
|
|
934
|
+
a = c - 1;
|
|
935
|
+
b = d;
|
|
936
|
+
e = 0;
|
|
937
|
+
f = 0;
|
|
938
|
+
newVerts.add(new Vec2D(c, d));
|
|
939
|
+
corners = 1;
|
|
940
|
+
while (true) {
|
|
941
|
+
bestAngleDif = MathUtils.TWO_PI;
|
|
942
|
+
for (i = 0; i < segs; i++) {
|
|
943
|
+
if (segments[i].x == c && segments[i].y == d
|
|
944
|
+
&& (segEnds[i].x != a || segEnds[i].y != b)) {
|
|
945
|
+
angleDif = lastAngle - segAngles[i];
|
|
946
|
+
while (angleDif >= MathUtils.TWO_PI) {
|
|
947
|
+
angleDif -= MathUtils.TWO_PI;
|
|
948
|
+
}
|
|
949
|
+
while (angleDif < 0) {
|
|
950
|
+
angleDif += MathUtils.TWO_PI;
|
|
951
|
+
}
|
|
952
|
+
if (angleDif < bestAngleDif) {
|
|
953
|
+
bestAngleDif = angleDif;
|
|
954
|
+
e = segEnds[i].x;
|
|
955
|
+
f = segEnds[i].y;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
if (segEnds[i].x == c && segEnds[i].y == d
|
|
959
|
+
&& (segments[i].x != a || segments[i].y != b)) {
|
|
960
|
+
angleDif = lastAngle - segAngles[i] + MathUtils.PI;
|
|
961
|
+
while (angleDif >= MathUtils.TWO_PI) {
|
|
962
|
+
angleDif -= MathUtils.TWO_PI;
|
|
963
|
+
}
|
|
964
|
+
while (angleDif < 0) {
|
|
965
|
+
angleDif += MathUtils.TWO_PI;
|
|
966
|
+
}
|
|
967
|
+
if (angleDif < bestAngleDif) {
|
|
968
|
+
bestAngleDif = angleDif;
|
|
969
|
+
e = segments[i].x;
|
|
970
|
+
f = segments[i].y;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
if (corners > 1 && c == newVerts.get(0).x && d == newVerts.get(0).y
|
|
975
|
+
&& e == newVerts.get(1).x && f == newVerts.get(1).y) {
|
|
976
|
+
corners--;
|
|
977
|
+
vertices = newVerts;
|
|
978
|
+
return true;
|
|
979
|
+
}
|
|
980
|
+
if (bestAngleDif == MathUtils.TWO_PI || corners == maxSegs) {
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
983
|
+
lastAngle -= bestAngleDif + MathUtils.PI;
|
|
984
|
+
newVerts.add(new Vec2D(e, f));
|
|
985
|
+
corners++;
|
|
986
|
+
a = c;
|
|
987
|
+
b = d;
|
|
988
|
+
c = e;
|
|
989
|
+
d = f;
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Only needed for {@link Shape2D} interface. Returns itself.
|
|
995
|
+
*
|
|
996
|
+
* @return itself
|
|
997
|
+
*/
|
|
998
|
+
public Polygon2D toPolygon2D() {
|
|
999
|
+
return this;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
*
|
|
1004
|
+
* @return
|
|
1005
|
+
*/
|
|
1006
|
+
public String toString() {
|
|
1007
|
+
StringBuilder buf = new StringBuilder();
|
|
1008
|
+
for (Iterator<Vec2D> i = vertices.iterator(); i.hasNext();) {
|
|
1009
|
+
buf.append(i.next().toString());
|
|
1010
|
+
if (i.hasNext()) {
|
|
1011
|
+
buf.append(", ");
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
return buf.toString();
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
*
|
|
1019
|
+
* @param x
|
|
1020
|
+
* @param y
|
|
1021
|
+
* @return
|
|
1022
|
+
*/
|
|
1023
|
+
public Polygon2D translate(float x, float y) {
|
|
1024
|
+
for (Vec2D v : vertices) {
|
|
1025
|
+
v.addSelf(x, y);
|
|
1026
|
+
}
|
|
1027
|
+
return this;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
/**
|
|
1031
|
+
*
|
|
1032
|
+
* @param offset
|
|
1033
|
+
* @return
|
|
1034
|
+
*/
|
|
1035
|
+
public Polygon2D translate(ReadonlyVec2D offset) {
|
|
1036
|
+
return translate(offset.x(), offset.y());
|
|
1037
|
+
}
|
|
1038
|
+
}
|