Skip to main content

geometry_kit_3d 0.1.0 copy "geometry_kit_3d: ^0.1.0" to clipboard
geometry_kit_3d: ^0.1.0 copied to clipboard

Immutable 3D solids for Dart — volume, surface area, ray picking, cross-sections, extrusion and revolution of geometry_kit 2D shapes, meshes with OBJ/STL export.

"Buy Me A Coffee"

geometry_kit_3d #

Immutable 3D solids for Dart, built on geometry_kit.

Box, sphere, cylinder, cone, solid of revolution and extruded prism on a grid, and a cone sliced by a tilted plane into an ellipse

Solids know their own volume, surface area and bounds, answer ray and containment queries, slice into 2D cross-sections, and export triangle meshes (OBJ, STL, or packed buffers for a GPU). Prisms and solids of revolution are built straight from geometry_kit 2D shapes. Pure Dart: runs in Flutter, on a server, or in a CLI.

For rendering in Flutter, see GeoScene in geometry_kit_widgets.

Conventions #

  • Right-handed coordinates, Y up.
  • Angles at the API are degrees (matching geometry_kit's rotate(deg)).
  • Everything is immutable: translate, scale and rotate return new solids.

Solids #

Solid Built from
Sphere centre, radius
Box centre, width × height × depth, Quaternion rotation
Cylinder base centre, axis, radius, height
Cone base centre, axis, radius, height
Prism a 2D Polygon extruded along a PlaneFrame
SolidOfRevolution a 2D profile (List<Point>) spun around an axis

Every solid implements Solid:

abstract interface class Solid {
  double get volume;
  double get surfaceArea;
  Aabb get bounds;
  bool contains(Point3 point);
  Point3 closestPoint(Point3 point);
  double distanceTo(Point3 point);
  RayHit? intersectRay(Ray3 ray);
  Solid translate({double x, double y, double z});
  Solid scale(double factor);
  Solid rotate(Point3 axis, double deg, {Point3 pivot});
  Mesh toMesh({int segments});
}

Cylinder, Cone and SolidOfRevolution share one exact implementation (RevolvedSolid): ray hits, containment and closest points are solved from the 2D profile, not from a mesh.

Examples #

Slice a cone #

final cone = Cone(baseCenter: Point3(0, -1, 0), radius: 1, height: 2);
final plane = Plane.fromPointNormal(
  Point3.zero,
  Point3.unitY.rotate(Point3.unitZ, 30),
);

final label = switch (cone.slice(plane)) {
  CircleSection(:final circle) => 'Circle r=${circle.radius}',
  EllipseSection(:final ellipse) => 'Ellipse ${ellipse.radiusX} × ${ellipse.radiusY}',
  ParabolaSection() => 'Parabola',
  HyperbolaSection() => 'Hyperbola',
  EmptySection() => 'No intersection',
};

Sections are geometry_kit 2D shapes in a frame lying in the plane: section.toPolygon() keeps working in 2D, section.toPoints3() maps the outline back to 3D.

Extrude a floor plan #

final plan = Polygon([
  Point(0, 0), Point(6, 0), Point(6, 3),
  Point(3.5, 3), Point(3.5, 5), Point(0, 5),
]);
final room = Prism.extrude(plan, height: 2.7);

print(plan.area);         // 25.0 m²   (2D API)
print(room.volume);       // 67.5 m³
print(room.lateralArea);  // 59.4 m² of wall

With the default PlaneFrame.ground, 2D x maps to 3D x and 2D y to 3D −z, so the plan looks like its drawing when seen from above.

Revolve a spline #

final profile = Spline([
  Point(0.55, 0), Point(0.85, 0.7), Point(0.32, 1.5), Point(0.5, 1.9),
]);
final vase = SolidOfRevolution(profile.sample(48));
print(vase.volume); // in dm³ = litres when units are decimetres

final mesh = vase.toMesh(segments: 48);
File('vase.stl').writeAsStringSync(mesh.toStl(name: 'vase'));

Pick and collide #

const camera = OrbitCamera(yaw: 30, pitch: 25, distance: 8);
final ray = camera.rayThrough(Point(tapX, tapY), width, height);
final hit = nearestHit(ray, solids);          // RayHit? with distance, point, normal

final drone = Sphere(center: dronePosition, radius: 0.3);
final touching = solids.where(drone.intersects); // exact, after a bounds check

Box.intersectsBox runs a separating-axis test for two oriented boxes.

Keep a pose with Transform3 #

Transforms on a solid are baked into its fields, so a box rotated twice cannot tell you how far it was turned. Keep the solid in local space and its placement in a Transform3 (position, rotation, uniform scale) when you need the pose itself, for example in an editor:

final crate = Box.cube(center: Point3.zero, size: 1); // local space
var pose = const Transform3(position: Point3(0, 0.5, 0));

pose = pose.rotated(Point3.unitY, 30).translated(const Point3(1, 0, 0));
final placed = pose.applyTo(crate);        // the solid in world space
final (:axis, :degrees) = pose.rotation.toAxisAngle(); // 30° about +Y

// Drag along the x axis: closest point on the axis to the pointer ray.
final onAxis = ray.closestPointOnLine(pose.position, Point3.unitX);

Math types #

Point3 (also used as a vector), Quaternion, Transform3, Ray3, Segment3, Plane, PlaneFrame, Aabb, Mesh, MeshBuilder, OrbitCamera.


"Buy Me A Coffee"

0
likes
160
points
125
downloads
screenshot

Documentation

API reference

Publisher

verified publishersamderlust.com

Weekly Downloads

Immutable 3D solids for Dart — volume, surface area, ray picking, cross-sections, extrusion and revolution of geometry_kit 2D shapes, meshes with OBJ/STL export.

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

#geometry #math #solid #mesh

License

MIT (license)

Dependencies

geometry_kit

More

Packages that depend on geometry_kit_3d