Bundle the first-party Python scripting plugin

This commit is contained in:
Felix Riestra committed 2026-09-19 23:16:27 +02:00
1 parent 889042c1f1
commit de51c1a17f
26 files changed
+11459 -27

No files matched your search

@@ -0,0 +1,54 @@
//! Create one disposable DWG with a paper layout and rectangular viewport.
//! Usage: cargo run --example create_layout_fixture -- /absolute/output.dwg
use std::io::Write;
use ocs_plugin_api::host::acadrust::{
entities::{Line, Text, Viewport},
CadDocument, DwgWriter, EntityType, Vector3,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let output = std::env::args().nth(1).ok_or("expected output DWG path")?;
let output = std::path::PathBuf::from(output);
let twisted = std::env::args().nth(2).as_deref() == Some("--twisted");
if output.extension().is_none_or(|value| value != "dwg")
|| output.parent().is_none_or(|parent| !parent.is_dir())
{
return Err("output must be a .dwg in an existing directory".into());
}
let mut document = CadDocument::new();
document.add_layout("Sheet")?;
document.add_entity(EntityType::Line(Line::from_coords(
-50.0, 0.0, 0.0, 50.0, 0.0, 0.0,
)))?;
document.add_entity_to_layout(
EntityType::Text(Text::with_value(
"Sheet note",
Vector3::new(20.0, 20.0, 0.0),
)),
"Sheet",
)?;
let mut viewport = Viewport::new();
viewport.id = 2;
viewport.center = Vector3::new(100.0, 80.0, 0.0);
viewport.width = 100.0;
viewport.height = 50.0;
viewport.view_height = 100.0;
viewport.view_center = Vector3::new(10.0, 20.0, 0.0);
viewport.view_target = Vector3::ZERO;
if twisted {
viewport.twist_angle = std::f64::consts::FRAC_PI_2;
viewport.view_target = Vector3::new(5.0, 10.0, 0.0);
}
document.add_entity_to_layout(EntityType::Viewport(viewport), "Sheet")?;
let mut file = std::fs::OpenOptions::new()
.write(true)
.create_new(true)
.open(&output)?;
DwgWriter::write_to_writer(&mut file, &document)?;
file.flush()?;
println!("Created {}", output.display());
Ok(())
}
@@ -0,0 +1,90 @@
# Example script for opencad-python's PY_RUN command.
#
# Historical experimental command-replay example. The portable default
# build omits ocs.command()/ocs.select(); nested replay may hang in OCS 2026.37.
#
# Draws two rectangles, a triangle, an arc, a pair of lines made parallel by
# a real PCONSTRAINT, a circle made tangent to a line by a real TCONSTRAINT,
# and a row of points added in one ocs.add_points() batch call, one of which
# gets an XDATA record written, read back, and removed. Every ocs.add_*/
# ocs.write_record call in one PY_RUN shares a script undo group. Command
# replay has separate host undo semantics.
#
# These ARE real, persistent constraint objects, not just geometrically
# correct-looking shapes: drag one of the two "parallel" lines afterward and
# the other follows to stay parallel, same for the tangent circle. Verified
# end-to-end (not just "it compiles"): after ocs.command("PCONSTRAINT") /
# ("TCONSTRAINT"), the geometry visibly changes and satisfies the
# relationship exactly (checked numerically — direction-vector cross
# product ~0 for parallel, center-to-line distance == radius for tangent).
#
# ocs.select(handles) + ocs.command(name) is what makes this possible:
# constraint commands read a prior *selection* (a "Select objects:"
# prompt), not picks fed as command-line tokens the way LINE's points are —
# select the entities first, then run the bare command name. See PLUGIN.md
# for the two host-side additions (HostApi::run_command / set_selection)
# this needed and exactly what they do and don't cover.
if not hasattr(ocs, "command") or not hasattr(ocs, "select"):
raise RuntimeError("This example requires experimental command replay host APIs")
def rectangle(x, y, w, h):
l1 = ocs.add_line(x, y, x + w, y)
l2 = ocs.add_line(x + w, y, x + w, y + h)
l3 = ocs.add_line(x + w, y + h, x, y + h)
l4 = ocs.add_line(x, y + h, x, y)
return [l1, l2, l3, l4]
# ── Two rectangles (not aligned to each other — see the note below) ───────
rectangle(0, 0, 20, 10)
rectangle(30, 2, 18, 9)
# ── A triangle (three lines closing a loop) ────────────────────────────────
ocs.add_line(60, 0, 75, 0)
ocs.add_line(75, 0, 67.5, 12)
ocs.add_line(67.5, 12, 60, 0)
# ── An arc, for variety ─────────────────────────────────────────────────────
ocs.add_arc(67.5, 20, 8, 0, 180)
# ── Two lines, deliberately NOT parallel — then made parallel by a real ───
# ── persistent PCONSTRAINT (not just recomputed coordinates).
line_a = ocs.add_line(0, 20, 20, 24)
line_b = ocs.add_line(0, 30, 20, 33)
ocs.select([line_a, line_b])
ocs.command("PCONSTRAINT")
# ── A line and a circle, deliberately NOT touching — then made tangent by ──
# ── a real persistent TCONSTRAINT.
tangent_line = ocs.add_line(0, 40, 20, 40)
tangent_circle = ocs.add_circle(10, 44, 3)
ocs.select([tangent_line, tangent_circle])
ocs.command("TCONSTRAINT")
# ── What's NOT in this script: aligning the two rectangles to each other ──
# ── (a real ALIGN, not just matching coordinates). Tried it separately:
# ── ocs.command("ALIGN <points...>") DOES apply the transform correctly,
# ── but ALIGN asks a trailing "Scale objects? [Yes/No]" question that
# ── ocs.command()'s point-feeding doesn't answer (it sends exactly one
# ── trailing Enter, and ALIGN needs two — one to skip the optional 3rd
# ── point pair, one for the scale question) — it leaves that prompt open
# ── instead of cleanly finishing. Left out of this script rather than
# ── shipping something that half-works; see PLUGIN.md for the detail.
# ── A batch of points, added in one HostApi::add_entities() call instead ──
# ── of one ocs.add_line-style call per point.
point_handles = ocs.add_points([[x, 50, 0] for x in range(0, 21, 2)])
# ── Tag one of those points with an XDATA record, read it back, then ───────
# ── remove it — round-tripping every supported value kind.
tagged = point_handles[0]
ocs.write_record(tagged, "OPENCAD_EXAMPLE", [
{"kind": "String", "value": "origin marker"},
{"kind": "Point3D", "value": [0.0, 50.0, 0.0]},
{"kind": "Integer32", "value": 1},
])
record = ocs.read_record(tagged, "OPENCAD_EXAMPLE")
assert record["app_name"] == "OPENCAD_EXAMPLE"
assert record["values"][0] == {"kind": "String", "value": "origin marker"}
ocs.remove_record(tagged, "OPENCAD_EXAMPLE")
assert ocs.read_record(tagged, "OPENCAD_EXAMPLE") is None