flutter3d_mesh 0.7.0
flutter3d_mesh: ^0.7.0 copied to clipboard
An editable mesh for flutter3d: half-edge topology on persistent typed arrays, selections, and the operations a modeller runs on them.
0.7.0 #
The first publication. The 0.6.0 below calls itself the first release, and
it was the first inside the workspace: it never reached pub.dev. 0.7.0 is the
number the whole shelf goes out on, so that one number names one tree and
^0.7.0 on any flutter3d_* package resolves against every other;
doc/boundary-0.7.0.md lists the thirteen packages that begin here. What
0.6.0 describes was a spike of three entries. The rest of the package is below.
-
Breaking against the spike.
EditMesh.halfEdgesOf(face)is gone; walk a face withforEachHalfEdge.verticesOfreturns aList<int>where it returned anIterable<int>.vertexCountandfaceCountcount live elements, and an id is a slot that may hold a deleted one.toMeshDatawas a flat-normal fan and now goes throughMeshLayoutPlan, so the vertex count of the same mesh differs.MeshData,RayandTriangleBvhcome frompackage:flutter3d_core/geometry.dart;flutter3d_geometrywas folded intoflutter3d_core, which is the one sibling this package depends on. -
EditMeshkeeps a journal, and deletion is a tombstone. Storage isJournalledFloatsandJournalledInts, andbeginStep/endStepbracket aJournalStepthat covers all nine arrays or none, so positions and topology cannot be undone to different versions.undoandredowalk it,abandonStepdrops a step without clearing redo, andjournalByteswithdropOldestJournalStepsserve a history budget counted in bytes. A journal of previous values measured 2% of a full copy where chunked copy-on-write measured 92 to 100% on scattered edits.deleteFaceanddeleteVertexleave slots behind so ids stay stable, andcompact()returns the packed mesh with anIdRemap. The mesh grows in place throughaddVertex,addFace,splitEdge,splitFace,weldTwinsandcutTwin. -
Attributes live in four domains and cost nothing until written.
MeshAttributenamesuv0,colour,weights,joints,crease,flagsandmaterialSlot. UVs are per corner, because a seam is two faces disagreeing about one vertex;EdgeFlags.sharpandEdgeFlags.seamare written to both halves of an edge;FaceFlags.smoothis per face. A layer nobody wrote answers a neutral value and is not allocated, and one created late is padded back to the journal's depth. -
Conversion to and from
MeshDatais three decisions made in the open.importMeshDatawelds by position withinmax(diagonal * 1e-6, 1e-9), rebuilds polygons and returns the mesh, anImportReportand the file's morph targets asShapeKeys. Going back,MeshLayoutPlan.buildear-clips concave faces, splits normals atMeshNormals.defaultSmoothAngleof 30 degrees, merges identical corners into one GPU vertex and keepsgpuVertexToVertex,gpuVertexToCornerandtriangleToFace. A kept plan refills positions without rebuilding: at 200 thousand triangles the full conversion measured 102 ms, a refill 6.7 ms and a refill of 40 vertices 1.5 µs. Import was quadratic, 6.6 s at 50 thousand triangles, and is 101 ms after the edge-key fix.toBytesandfromBytesare a little-endian tagged format that skips sections it does not know, andeditInIsolateruns aMeshWorkon another isolate, or inline on the web. -
Normals.
MeshNormals.buildweights by corner angle, without which a cube corner moves by 19 degrees depending on how the cube was triangulated, and breaks averaging at a face that is not smooth, a sharp edge or an angle past the threshold.rebuildAroundrecomputes only the fans around moved vertices.flipNormalsandmakeConsistentare onEditMesh;makeConsistentleaves an open island as it found it. -
Selectionis a value outside the journal, so undo does not undo a click. It has anElementLevelof vertex, edge or face, an edge id is the smaller of its two half-edges, and it offersunion,difference,intersection,toggle,convertedTo,grown,shrunk,linked,boundary,Selection.edgeLoop,Selection.edgeRingandSelection.byMaterialSlot. A loop steps only through valence 4 and a ring only across quads. -
Operations take
(mesh, selection, params)and return anOpResult. A refusal isOpResult.refusedwith a sentence.translateSelection,rotateSelection,scaleSelectionandtransformSelectionpivot onmedianOf;extrudeFacesbuilds no interior walls for a region;loopCut,insetFaces,bevelEdges,bevelVertices,bridgeLoops,slideEdges,triangulateFaces,deleteSelection,duplicateSelectionandsplitSelectionare the rest of that shape. Others are shaped differently:mergeByDistanceandmergeAtrebuild the mesh and return it with aMergeReport,separateComponentsreturns a mesh and anIdRemapper island, anddissolveEdgeanddissolveVertexare methods onEditMesh. Extruding one face of a box gives 12 vertices, 20 edges and 10 faces with the Euler characteristic still 2. Not built: a bevel with more than one segment, which is refused in a sentence, a knife, andinsetFaces(individual:), which is accepted and has no effect. -
Checks, holes and picking.
MeshChecks.allreports n-gons, boundary edges, non-manifold vertices, isolated vertices, degenerate faces, duplicate vertices and inverted shells asMeshIssues that each carry aselection.fillHoles(mesh)closes every boundary chain and returns how many it closed.MeshBvhanswersraycast,facesInAabbandfacesInFrustumand canrefitwithout a rebuild: at 200 thousand triangles a rebuild measured 55 ms, a refit 5 ms and one ray 0.5 µs, and 10 000 random rays agree with a brute-force scan.MeshPickerfindsfaceAt,vertexNearandedgeNear. -
Parametric shapes, recipes and fixtures.
ParametricShapeis sealed overParametricCuboid,ParametricPlane,ParametricLathe,ParametricSphere,ParametricCylinderandParametricTorus; each builds quads with sharp rings where the engine's primitives duplicate vertices, a flat cap is one n-gon, and the UVs match the engine's corner for corner.recipes()lists sixteen builders in fourRecipeCategorys, fromfloorLamptowindow, at real dimensions.package:flutter3d_mesh/testing.dartis a second library with eight fixture meshes. -
Modifiers.
Modifieris sealed overArrayModifier,MirrorModifier,SmoothModifier,SubdivisionModifierandBooleanModifier, each withtoJson;modifierFromJsonanswers null for a kind it does not know.ModifierStack.evaluateis memoized on the identity of the base and of the operands, since an operand can change while the base does not.catmullClarkhonours semi-sharp creases and interpolates UVs per face so seams stay, and ten passes ofsmoothVerticeswithpreserveVolumeshrink a sphere's mean radius by 0.04% where plain Laplacian smoothing shrinks it by 26%.mirror(bisect: true)throwsUnimplementedError, and the array and mirror modifiers carry positions, topology and material slots and no UVs, colours, creases or skin. -
Booleans.
booleanUnion,booleanSubtractandbooleanIntersectreturn aCsgResultor null pastmaxPolygons, 200 000 by default, checked before the work and again on the result. The BSP runs on its own stack so a deep tree does not overflow the native one. Coplanar polygons are counted intoCsgResult.warnings. The result can contain T-junctions, so it may not be watertight; its volume is right. -
UV.
projectUvwithUvProjection.planaror.box,splitIslandsalong seams,lscmsolved by conjugate gradients with a Jacobi preconditioner,unwrapMeshwithonProgressandisCancelled,stretchOfreading 1.0 for an isometry, andpackIslands, a shelf packer that may rotate by 90 degrees. -
Skin weights and shape keys.
paintWeight,normalizeVertexWeights,pruneVertexWeights,mirrorWeights,smoothVertexWeightsandgradientWeightsedit theweightsandjointslayers, andlimitInfluencesholds them to a count. AShapeKeyis a set of positions thatgrownToandremappedBykeep in step with the mesh, andshapeKeyMorphTargetsemits one delta per GPU row of aMeshLayoutPlan. -
Simplification, remeshing and collision shapes.
simplifyMeshandsimplifyMeshWithAttributestake and returnMeshData, the second holding borders withboundaryWeight.isotropicRemeshandretopologizereturn anEditMesh; the remesher assumes a closed mesh and does not project back onto its source.computeConvexHull,decomposeConvexwith a default of 12 pieces,fitBoxByInertia,fitSphereByInertia,fitCapsuleByInertiaandstaticMeshShapemake what a physics world needs. There is no LOD chain in this package;LodSpecisflutter3d_model_core's. -
Sculpting, multiresolution and baking.
SculptMeshstores a mesh in chunks of 1024 vertices withdirtyChunks, so a stroke re-uploads what it touched.applyBrushStroketakes aBrushof eightBrushKinds and threeBrushFalloffs, withsymmetryX.Multireskeeps levels withmeshAt,displaceanddisplacementMap; a million vertices over four levels measured 4.1 s.bakeNormalMap,bakeAmbientOcclusion,bakeCurvatureandbakeThicknessfill aBakedMapthroughrasterizeUv, anddilatepads the islands.projectBrushmaps a 3D brush toUvSpans for texture paint. Dynamic topology is not built. -
Plain Dart. Dependencies are
flutter3d_core^0.7.0andvector_math. The archive carriesskills/flutter3d-mesh-editing-topology/for a coding agent, installed withdart run skills@ get.
0.6.0 #
The first release, taking the set's number rather than a first of its own, because the whole workspace goes out together.
-
EditMesh: half-edge topology overInt32ListandFloat32List. Faces of any valency, twins matched by the pair of vertices an edge runs between, boundaries answered as boundaries rather than as a face that is not there. -
A cuboid as six quads, an extrusion, and the way back to
MeshData. Quads and not triangles, which is the point: a loop cut through a triangulated cube has nothing to cut along. -
validate()and the Euler characteristic, because every invariant here has been broken silently by an operation in some modeller — anextthat leaves its face, a twin that is not mutual — and each is invisible until a loop walk runs forever.