Geometry¶
Mesh baking needs only the standard library; CAD-solid export needs the
cad extra (pip install "longeron[cad]").
Parametric 3D geometry for architecture mixes (spike).
Builds to-scale UAVs from a mix’s catalog attribute values with plain
triangle meshes (stdlib math only). Four airframe families are
supported: the N-arm multirotor (drone_geometry() – arms every
360 / N degrees, sized from prop diameter + tip clearance; a 3-arm
frame grows a tail boom, coaxial=True stacks counter-rotating motor
pairs on every arm, and the default is the classic quad), the
streamlined teardrop-body quad
(teardrop_quad_geometry() – a lathed low-drag bullet stood on end,
its long axis normal to the planar rotor quad around it), the cruciform
tail-sitter VTOL (winged_vtol_geometry() – a minimal lathed
fuselage, two unswept airfoil wing pairs in a + cruciform, and one
tractor rotor on each of the four wingtips with every thrust axis
parallel to the chords/body axis; baked nose-up in its hover attitude),
and the streamlined interceptor
(interceptor_geometry() – slender lathed fuselage, thin unswept
NACA-0009 wing, cruciform tail, pusher prop). The tailless flying wing
(flying_wing_geometry()) is the one family drawn with real
stability-and-control geometry: quarter-chord sweep, tip washout, and a
parametric reflexed section, straight from its model attributes. Every
lifting surface is
lofted from a real NACA 4-digit section (naca4_profile()), not a
rectangular slab. Motor cylinders come from motor mass
(solid-cylinder density heuristic), prop disks from diameter, and the
battery box from battery mass (LiPo density + brick proportions). One
call turns a configuration into the mesh dict
longeron.widgets.viewer3d paints:
{"unit": "m",
"parts": [{"name", "color", "opacity",
"vertices": [x, y, z, ...], "faces": [i, j, k, ...]}, ...],
"bounds": [[xmin, ymin, zmin], [xmax, ymax, zmax]]}
A part may additionally carry a key – a stable model identity (by
convention the SysML part usage’s qualified name) stamped by
tag_parts() – which the viewer uses for linked selection
(longeron.analysis.link); untagged parts fall back to their
name.
The same geometry feeds the GEOMETRIC REQUIREMENT CHECKS, which are
CAD-NATIVE: camera_occlusion() builds a VIEW CONE solid at a
mounted camera (apex at the lens, axis along the boresight, half-angle
fieldOfView / 2) and boolean-INTERSECTS it with every other
component’s parametric solid – the same solids to_cadquery()
builds – reporting intersected volume over cone volume (0.0 is a
perfectly clear view), and disc_overlap() boolean-intersects each
propeller disc (a thin cylinder solid, stamped analytically by
drone_geometry() in split_instances mode) with every other
component, reporting the overlap volume (0.0 is no overlap). cadquery
(the cad extra) powers the exact booleans; without it both checks
fall back to a deterministic stdlib volume quadrature over the mesh
triangles that integrates the SAME measures (see
occlusion_report() / overlap_report() for the accuracy
contract). Both are deterministic and keyed by geometry_checks()
to feed the DeepScout program’s installation requirements
(examples/deepscout/aircraft.sysml) through the scoreboard’s
values= seam in either posture.
House pattern: MESH geometry is baked in Python once per configuration
(a millisecond or so – no CAD kernel in the render loop); the front-end
never recomputes it. Y is up, +X is forward, one unit is one metre, and every
dimension is a real measurement or a documented heuristic, so two mixes
render truly to scale side by side (lineup() merges several
configurations into one to-scale scene). mission_geometry()
dispatches a UavMissions-style mix onto its family builder from the
selected airframe’s attributes.
to_cadquery() rebuilds the quad-copter assembly as CAD solids
(STEP export, and the solid source of the CAD-native checks above)
behind the cad extra – the mesh pipeline here deliberately does not
need the ~1 GB OCC kernel.
- longeron.analysis.geometry.BayShape¶
the payload-bay shape vocabulary of the DeepScout
Airframedef (examples/deepscout/aircraft.sysml– thebayShapeattribute): a"box"slung under a rotor hub, a"hull"bay inside a lathed fuselage, or an"ogive"pod blended at a flying wing’s rootalias of
Literal[‘box’, ‘hull’, ‘ogive’]
- longeron.analysis.geometry.GeometryEngine¶
which implementation the interference/occlusion checks run on:
"cad"(exact booleans on the OCC kernel, behind thecadextra),"mesh"(the dependency-free sampled fallback), or"auto"(cad when importable, mesh otherwise – the honest default)alias of
Literal[‘auto’, ‘cad’, ‘mesh’]
- longeron.analysis.geometry.airframe_geometry(*, wing_span, wing_area, taper, fuselage_length, motor_count, arm_count, prop_diameter, motor_mass, battery_mass, fc_mass=None, esc_mass=0.014, arm_thickness=None, arm_width=None, sweep_deg=0.0, washout_deg=0.0, wing_section=None, center_section_span=0.0, pod_length=None, pod_diameter=0.05, pod_station=None, fuselage_diameter=None, tail_area=None, tilt_pivot_setback=None, tilt_arm=None, nose_arm=None, tilt_deg=None, bay_shape=None, bay_length=None, bay_width=None, bay_height=None, camera=None)[source]¶
Family-dispatched geometry from airframe-shell attribute values.
The keyword names mirror the geometry knobs of the DeepScout
Airframedef (examples/deepscout/aircraft.sysml) plus the propulsion sizes a mix or a display default supplies. The dispatch ladder picks the builder: no wing and no fuselage ->drone_geometry()(the N-arm multirotor –arm_countsets the frame family, and a station count of twice the arm count stacks the coaxial pairs); no wing but a real fuselage ->teardrop_quad_geometry()(the upended bullet); a wing with no fuselage ->flying_wing_geometry()(the tailless family: the wing IS the fuselage); a single motor station ->interceptor_geometry(); a wing AND a fuselage with a declared tilt-pivot chain (tilt_arm) ->tilt_tri_geometry()(the convertible tilt-rotor tri, rendered attilt_deg– hover attitude by default, the tail-sitter’s convention); otherwisewinged_vtol_geometry()(the cruciform tail-sitter, rendered in hover attitude).arm_thickness/arm_widthdraw the quad families’ arms at a load-sized tube diameter when given;esc_massis the drone branch’s 30.5 mm stack heuristic. Two callers feed this ladder:mission_geometry()from a mission-catalog mix, andlongeron.analysis.grand.scene_for()from a fleet airframe definition’s own attributes. The tailless S&C and pusher-installation knobs (sweep_deg,washout_deg,wing_section,center_section_span,pod_length,pod_diameter,pod_station) reachflying_wing_geometry()only – the flying wings are the one family whose model declares them; every other loft keeps its zero-sweep planform until the loft framework generalizes – and the tilt-pivot knobs (tilt_pivot_setback,tilt_arm,nose_arm,tilt_deg, withfuselage_diameterandtail_areariding along) reachtilt_tri_geometry()only, the one family whose model declares a pivot chain. The declared payload bay rides along per family (bay_shapeand dimensions, theAirframedef’sbayShapevocabulary –BayShape): a"box"reaches the multirotor builder as the slung box, an"ogive"reaches the flying wings as the blended pod, and a"hull"bay needs no knobs – the lathed builders draw their hull bays from the fuselage they already have.cameramounts the mission camera on the multirotor builder (the other builders draw their own camera bodies at fixed stations).
- longeron.analysis.geometry.architecture_geometry(study, architecture, **overrides)[source]¶
drone_geometry()for a mix (seearchitecture_params()).
- longeron.analysis.geometry.architecture_params(study, architecture)[source]¶
Geometry inputs from a drone-catalog mix.
Expects the
TradeQuad-style variation pointsmotors(attributemass),props(diameterIn),battery(mass), andesc(mass) – this is demo-grade wiring for the drone catalog, not a generic geometry mapping.
- longeron.analysis.geometry.camera_occlusion(mesh, camera=None, *, sensing_range=None, resolution=24, exclude=('camera',), engine='auto')[source]¶
The airframe volume inside the view cone over the cone volume.
The scalar measure behind the
clearViewrequirement of the DeepScout program (“occludedFraction”): 0.0 is a perfectly clear view cone, anything positive means some component pokes into it. Seeocclusion_report()for the cone construction, the engines, and the per-part offender breakdown.- Return type:
- longeron.analysis.geometry.disc_overlap(mesh, *, resolution=24, engine='auto')[source]¶
The total propeller-disc overlap volume, in cubic metres.
The scalar measure behind the
propClearancerequirement of the DeepScout program (“discOverlapVolume”): the sum over every disc ofoverlap_report()’s per-disc overlap. 0.0 means no disc touches anything; a disc-against-disc overlap counts once per participating disc.- Return type:
- longeron.analysis.geometry.drone_geometry(*, prop_diameter_in, motor_mass, battery_mass, esc_mass, fc_mass=None, arm_count=4, coaxial=False, arm_thickness=0.005, arm_width=0.013, segments=24, split_instances=False, motor_spacing=None, camera=None, bay_length=None, bay_width=None, bay_height=None)[source]¶
A to-scale multirotor mesh dict from catalog attribute values.
arm_countsets the frame family: arms radiate every360 / Ndegrees (_arm_angles()– the “X” convention, nose clear), a 3-arm frame stretches its single rear arm into the tail boom (_TRI_BOOM_RATIO), and the default 4 reproduces the classic quad byte-for-byte.coaxialstacks a counter-rotating pair on every arm: the upper motor rides the arm top exactly like the flat build, the lower hangs_COAX_DROPbeneath it on a drawn standoff post, and its prop disc spins below – two discs per arm, both stamped insplit_instancesmode.The frame is derived: adjacent motors sit one prop diameter plus
_PROP_CLEARANCEapart (for any N, on the circle that spacing implies), so a 10-inch cruiser genuinely dwarfs a 5-inch racer and a hexa is honestly wider than a quad.motor_spacingoverrides that derivation with a FIXED adjacent motor-to-motor distance – a real frame does not grow when a bigger prop is bolted onto it, so a prop-swap what-if passes the stock spacing and letsdisc_overlap()judge the result.arm_thickness/arm_widthdefault to the demo heuristics; callers with load-sized arm tubes (seemission_geometry()) pass the sized outer diameter so heavier- loaded designs genuinely look beefier. Parts of one kind merge into a single mesh (one draw call each in the viewer).split_instanceskeeps the motor and prop instances as separate parts –motor1..motorRandprop1..propRforRrotors, the same names and order as theto_cadquery()assembly children (coaxial builds count the uppers first, arm by arm, then the lowers in the same arm order) – so each can carry its own identity key (e.g. an M0 individual id, seetag_parts()) for per-instance linked selection. The geometry is a pure re-partition: concatenating the instance parts reproduces the merged part exactly, and the default (False) output is unchanged. Split mode additionally stamps the analytic propeller discs onto the mesh (mesh["discs"]: centre, normal, radius, thickness, owning part, and the same-station parts an overlap check must ignore) – the inputoverlap_report()consumes – and the parametric recipe (mesh["cad"]: this function’s own sizing inputs) from which the CAD-native checks rebuild the exact solids viato_cadquery().cameramounts the mission camera: a mapping with the placement and boresight attribute names of the DeepScout program’sScoutParts::F450Kit::Camerapart (x/y/zmetres from the top-plate centre,azimuth/elevation/fieldOfViewdegrees), typically the slot dict of an instantiated/interpreted camera individual. It adds a violetcamerabody part (a small box, yawed to the azimuth) and stamps the parameters onmesh["camera"]forcamera_occlusion()’s view cone.The hub owns its structure: a bottom plate closes the plate stack (the battery straps to it at its unchanged station) and one arm-root gusset per arm grows out of the plates (
_hub_gussets()) – the drawn twin of the mission sizing’sgussetMass.fc_massstacks the flight-controller board above the ESC on the 30.5 mm pattern (its own slatefcpart, so the FC is selectable).bay_length/bay_width/bay_heightsling the declared payload-bay box under the battery (a fleet airframe’sbayShape "box"– seeairframe_geometry()); a coaxial build drops the box below its lower disc plane. All three default toNone: no bay part.
- longeron.analysis.geometry.flying_wing_geometry(*, wing_span, wing_area, taper, motor_count, prop_diameter, motor_mass, battery_mass, fc_mass=None, sweep_deg=0.0, washout_deg=0.0, section='0015', center_section_span=0.0, pod_length=None, pod_diameter=0.05, pod_station=None, bay_length=None, bay_diameter=None, segments=24)[source]¶
A to-scale tailless flying wing: swept panels, trailing-edge pushers.
The wing IS the airframe (the DeepScout
FlyingWingsconvention:fuselageLength0 marks the family): straight-tapered panels lofted fromsection(a NACA 4-digit code or a"reflexed"-prefixed one, see_lift_surface()), the quarter-chord line raked aft bysweep_degand the tips twisted down bywashout_deg; a winglet rides each swept tip, raked with the wing. Withcenter_section_span> 0 the planform is CRANKED – the standard flying-wing pusher installation: the trailing edge runs FLAT across the center section (level with the crank station’s trailing edge) so a root pusher disc tucks behind it, and only the outer panels carry the swept trailing edge (and the washout: a cranked center bay flies untwisted).Every motor station hangs a pusher pod on the local trailing edge: a nacelle of
pod_diameterrunningpod_lengthstraight aft, the motor can flush at its tail and the prop disc just behind it.pod_stationplaces the stations: the model’s declaredpodStation(metres from the centerline, mirrored per side) – the tip-prop twin flows its wingtip stations through here exactly the way sweep flows.Nonekeeps the display default, a symmetric row across the middle third of the span (which is the root twin’s declared station).pod_lengthNone derives the clearance length locally – base housing + disc clearance + the trailing edge’s aft rise across the disc’s own span extent – so the disc plane clears the wing for ANY prop; the model-driven callers pass the declaredpodLength(derived the same way inexamples/deepscout/flyingwing.sysmlfor the branch’s reference disc). The planform knobs are the model’s own declared attributes (sweepDeg/washoutDeg/centerSectionSpan/ the reflexed 15% bay section), so the drawn planform IS the declared one.The payload bay is a blended OGIVE pod on the root’s belly (
bay_lengthxbay_diameter, the model’sbayLength/bayWidth; None sizes a display pod from the battery brick): a body of revolution whose crown rides up inside the root section, round nose just ahead of the leading edge, fine tail well clear of the pusher discs. The battery brick, the flight-controller board, and the mission camera ride INSIDE it at their true stations – their own selectable parts – replacing the naked battery sleeve this family used to poke through its own skin. One stand-in remains, named so nobody reads more than the mesh knows: the reflexed camber line is a simple parametric S-camber, not a catalog airfoil, and the panels loft straight (C0 at the crank) – the blended center body this family really flies is a job for the loft framework’s lofted wing body.
- longeron.analysis.geometry.geometry_checks(mesh, *, sensing_range=None, resolution=24, engine='auto')[source]¶
Both geometric requirement measures, keyed for the scoreboard.
Returns
{"occludedFraction": ..., "discOverlapVolume": ...}– exactly the free names theinstallationrequirements of the DeepScout program measure, so the result feedsscoreboard(model, values=geometry_checks(mesh))directly (the lightest honest wiring: the measures are computed kernel-side from the same parametric geometry the 3D viewer paints, then injected as evaluation-frame bindings; nothing is baked into the model file). Needs a mesh built withdrone_geometry(split_instances=True, camera=...). Both measures read 0.0 for a clean installation in BOTH engines (seeocclusion_report()for the engine contract).
- longeron.analysis.geometry.interceptor_geometry(*, body_length, wing_span, wing_area, taper, prop_diameter, motor_mass, battery_mass, fc_mass=None, segments=24)[source]¶
A to-scale streamlined interceptor: slender body, pusher prop.
The fuselage is a lathed low-drag body just wide enough for the battery brick; the wing is a thin unswept NACA-0009 loft (chord =
wing_area / wing_span, straight quarter-chord) at mid-body, the cruciform tail fins carry the same section, and the single catalog prop pushes at the stern. The battery bay is drawn as an indigo sleeve around the fuselage at its true length and position (the brick rides inside the body).
- longeron.analysis.geometry.lineup(meshes, *, gap=0.25, labels=None)[source]¶
Merge mesh dicts into one to-scale scene on a shared ground plane.
Each mesh keeps its parts (names prefixed by its label so a scene can carry several configurations); everything sits on a shared ground plane (ymin aligned) with
gapmetres between neighbouring cells. Up to three meshes pack side by side along X at their true widths; larger lineups fold into the adaptive grid of_grid_shape()(rows along Z, row-major from the front, uniform cells sized by the largest footprint) so four configurations read as 2x2, six as 2x3, eight as 2x4. Withlabelsthe scene carries alabelslist ({text, anchor}) thatlongeron.widgets.viewer3d.mesh_viewer()renders as a billboard caption above each cell.
- longeron.analysis.geometry.mission_geometry(study, architecture, **overrides)[source]¶
Family-dispatched geometry for a mission-catalog mix.
The selected airframe’s attributes feed
airframe_geometry(), whose dispatch ladder picks the family builder. When the mix’s metrics carry the load-sizedarmOuterDiameter(the assembly’s structural sizing), the quad families draw their arms at that diameter – a sprint-motor aluminum build genuinely looks beefier than a carbon eco build.
- longeron.analysis.geometry.mission_params(study, architecture)[source]¶
Geometry inputs from a mission-catalog mix.
Expects variation points
airframe(attributeswingSpan,wingArea,taper,fuselageLength,motorCount,armCount),motors(mass),props(diameter), andbattery(mass) – the convention of the DeepScout mission catalog (examples/deepscout/missions.sysml). The tailless S&C knobs (sweepDeg,washoutDeg,wingSection) ride along when the selected airframe declares them and default to the unswept legacy planform when it does not.
- longeron.analysis.geometry.naca4_profile(code='2412', points=24)[source]¶
A closed NACA 4-digit section as chord-normalized
(x, y)pairs.Cosine-spaced stations, closed trailing edge (the -0.1036 thickness coefficient), ordered TE -> upper surface -> LE -> lower surface -> TE, i.e. counter-clockwise in the chord plane.
pointsis the total vertex count of the closed polygon (~24 is plenty for a mesh loft).
- longeron.analysis.geometry.occlusion_report(mesh, camera=None, *, sensing_range=None, resolution=24, exclude=('camera',), engine='auto')[source]¶
How much of the camera’s view cone the airframe fills, and what.
The check is CAD-NATIVE: a VIEW CONE solid – apex at the camera position, axis along the azimuth/elevation boresight, half-angle
fieldOfView / 2, reachingsensing_rangemetres (default: the airframe bounding-box diagonal, long enough to sweep past the whole craft) – is boolean-intersected with every other component’s parametric solid, the same solidsto_cadquery()builds. A perfectly clear view intersects nothing. Returns:{"engine": "cad" | "mesh", "occludedFraction": intersected volume / cone volume, "occludedVolume": ..., # m^3, union of all offenders "coneVolume": ..., "sensingRange": ..., "obstructions": {part: m^3, ...}} # offenders, largest first
enginepicks the implementation."cad"(exact booleans) needs thecadextra and a mesh built bydrone_geometry(split_instances=True)(which stamps the parametric recipe the solids are rebuilt from);"mesh"is a stdlib fallback that integrates the SAME measure by deterministic volume quadrature – ray-parity point-in-solid tests over an exact cell decomposition of the cone – against the mesh triangles. The default"auto"uses CAD when both prerequisites hold. The quadrature is exact for a clear cone (every weight counted is a genuine interior point) but can MISS features thinner than a grid cell (resolutionaxial slabs); treat its nonzero readings as real and its zeros as “nothing grid-cell-sized”. CAD per-part volumes are each exact (parts that interpenetrate each other are counted once per part); the mesh engine attributes each cell to the first part (mesh order) containing it.cameradefaults to the parameters stamped onmesh["camera"]bydrone_geometry(); pass an explicit mapping (theCamerapart’s attribute names) for what-ifs – e.g. the same camera yawedazimuth=180to look back through the airframe.excludenames mesh parts the cone may legitimately contain (the camera’s own body, whose centre is the cone apex). Deterministic in both engines: equal inputs give equal fractions.
- longeron.analysis.geometry.overlap_report(mesh, *, resolution=24, engine='auto')[source]¶
Per-disc overlap: how much of each propeller disc is inside what.
The CAD-native reading of “propeller discs shall not overlap other components”: each disc is a thin cylinder solid (the assembly’s own prop cylinders), boolean-intersected with every other component’s solid. Consumes the analytic discs
drone_geometry()stamps onmesh["discs"]insplit_instancesmode (a disc knows which same-station parts – its own prop and motor can – to ignore). Returns one row per disc, ordered as stamped:{"disc": "prop1", "engine": "cad", "overlap": 0.0, # m^3 "parts": {offender: m^3, ...}} # largest first
overlapis the volume of the disc’s intersection with the UNION of the non-excluded components – exactly 0.0 when the disc is clear. Engines as inocclusion_report():"cad"computes exact booleans on the parametric solids (needs thecadextra + the stamped recipe);"mesh"estimates the same volumes by deterministic mid-plane quadrature over the stamped disc (exact-zero when clear, may miss sub-cell slivers);"auto"prefers CAD.
- longeron.analysis.geometry.tag_parts(mesh, mapping, *, strict=True)[source]¶
Stamp model identities onto mesh parts (linked-selection plumbing).
Returns a copy of
meshwhose parts named inmappingcarry akey– by convention the qualified name of the model part the component renders, or, for per-instance parts (seedrone_geometry()’ssplit_instances), the M0 individual id fromlongeron.m0.interpret()(Rotorcraft::QuadCopter#0. motors#2), whose dotted path derives the owning usage for linked selection (longeron.analysis.link.individual_qname()). Several mesh parts may share one key, and parts not named keep no key and fall back to theirnameas their identity inlongeron.widgets.viewer3d. Vertex and face arrays are shared with the input, not copied.With
strict(the default) every mapping entry must name a part, so typos fail loudly; passstrict=Falseto reuse one mapping across airframe families with different part sets.lineup()carries keys through unchanged (its label prefixes only rename), so tag each configuration before merging and a selection lights up in every cell.
- longeron.analysis.geometry.teardrop_quad_geometry(*, fuselage_length, prop_diameter, motor_mass, battery_mass, fc_mass=None, arm_thickness=0.005, arm_width=0.013, segments=24)[source]¶
A to-scale streamlined teardrop-body quad (wingless dash bird).
The shell is a body of revolution lathed from the NACA-0025 half-thickness curve and stood on end: its long axis is NORMAL to the rotor plane – the bullet pierces the disk plane blunt-nose-up, fine tail down – so in a dash (the whole quad pitched over) the body flies point-first with minimal frontal area. Four arms radiate horizontally from the widest station to the lift motors, keeping the four prop disks a planar quad around the body (surface normals +Y, parallel to the body axis) with genuine radial clearance to the hull. The battery is drawn as an indigo sleeve at its true position inside the shell.
- longeron.analysis.geometry.tilt_tri_geometry(*, wing_span, wing_area, taper, fuselage_length, prop_diameter, motor_mass, battery_mass, fc_mass=None, fuselage_diameter=None, tail_area=None, sweep_deg=0.0, section='2412', pod_diameter=0.05, tilt_pivot_setback=0.04, tilt_arm=0.09, nose_arm=0.1, nose_pivot_setback=0.0, tilt_deg=90.0, segments=24)[source]¶
A to-scale tilt-rotor tri-wing at a commanded tilt angle.
The convertible of the fleet (the DeepScout
TiltRotorsbranch): a lathed fuselage, one swept tapered wing lofted fromsection, a conventional tail (tailplane pair + fin, NACA 0009, sized fromtail_area), and THREE pivoting motor stations – a pusher pod on each wingtip trailing edge (Arc B’s tip-pusher installation) and a tractor at the nose.tilt_degis the conversion state and a first-class scene parameter: 0 is cruise (tip discs vertical behind the tips, nose disc vertical ahead of the apex), 90 is hover (the tip pods swing DOWN so their discs hang level under the tips, the nose unit swings UP so its disc rides level over the nose), and any angle between renders the conversion mid-arc, so the interference gate can sample the whole sweep.The pivots are the model’s own declared chain: each tip pivot sits
tilt_pivot_setbackaft of the LOCAL trailing edge (inside the pod, never inside the wing) with its disctilt_armfurther aft; the nose pivot sitsnose_pivot_setbackaft of the fuselage apex (0 in the declared geometry) with its discnose_armahead. Pivot-outside-the-surface is the conversion-clearance guarantee – every disc point stays at least its arm’s length from its pivot through the sweep – and the setback knobs exist precisely so a test can BREAK the guarantee (bury a pivot) and prove the interference oracle catches it. The battery brick, the flight-controller board, and the mission camera ride the hull at their true stations as selectable parts;fuselage_diameterdraws the hull at the model’s declared width (Nonefalls back to the battery-brick heuristic the other lathed builders use).
- longeron.analysis.geometry.to_cadquery(*, prop_diameter_in, motor_mass, battery_mass, esc_mass, fc_mass=None, arm_count=4, coaxial=False, arm_thickness=0.005, arm_width=0.013, motor_spacing=None, camera=None, bay_length=None, bay_width=None, bay_height=None)[source]¶
The same parametric assembly as cadquery solids (
cadextra).Returns a
cadquery.Assemblywith one named, colored child per part – ready forassembly.export("drone.step")or downstream CAD – built from the same sizing inputs asdrone_geometry()(arm_count/coaxialpick the frame family and the coax stacking,motor_spacingfixes the adjacent motor-to-motor distance for prop-swap what-ifs,cameramounts the mission camera body and takes theCamerapart’s attribute names). The child names and order matchdrone_geometry’ssplit_instancesparts (coax lowers follow the uppers). These exact solids are what the CAD-native geometric checks (occlusion_report()/overlap_report()) boolean-intersect. Kept separate from the mesh pipeline so the viewer never depends on the OCC kernel.- Return type:
- longeron.analysis.geometry.view_cone(camera, *, length)[source]¶
The camera’s view cone as a cadquery solid (
cadextra).Apex at the camera position, axis along the azimuth/elevation boresight, half-angle
fieldOfView / 2, truncatedlengthmetres from the apex (the sensing range under test). This is the solid the CAD-native occlusion check boolean-intersects with the airframe: any non-empty intersection is an obstruction.camerauses theCamerapart’s attribute names (seeocclusion_report()).- Return type:
- longeron.analysis.geometry.winged_vtol_geometry(*, wing_span, wing_area, taper, fuselage_length, prop_diameter, motor_mass, battery_mass, fc_mass=None, segments=24)[source]¶
A to-scale cruciform tail-sitter VTOL, baked in its hover attitude.
The craft is assembled nose-along-+X (the cruise frame) and then stood on its tail (+X -> +Y), so the scene reads as hover: a minimal slender lathed fuselage; two unswept airfoil-lofted wing pairs in a
+cruciform – the main NACA-2412 pair spanswing_spanwith chord =wing_area / wing_spantapering about a straight quarter-chord, the secondary NACA-0009 pair is_SECONDARY_SPAN_RATIOof that span at the same chords – and one tractor rotor on each of the four wingtips, every thrust axis PARALLEL to the wing chords and the body axis. In hover the vehicle hangs nose-up on its four (now horizontal) disks; for cruise the whole craft pitches over and flies wing-borne. No booms and no separate tail: the cruciform panels are the tail. The main-pair tips carry the catalog props, the secondary tips_SECONDARY_PROP_RATIO-scaled ones (that ratio is baked into the catalog’s diskAreaFactor). Fuselage radius follows the battery brick, drawn as an indigo sleeve at its true station.