From d074c42a34aef5158303954addea8060127dc17e Mon Sep 17 00:00:00 2001
From: Peter Wolf <84736182+peterwolf-pl@users.noreply.github.com>
Date: Sat, 3 Jan 2026 08:25:47 +0100
Subject: [PATCH 1/5] Add DXF to ACODE conversion tab with arc segmentation
---
acode.py | 43 ++++++++-
templates/index.html | 204 +++++++++++++++++++++++++++++++++++++++++++
ui_sender.py | 150 +++++++++++++++++++++++++++++++
3 files changed, 395 insertions(+), 2 deletions(-)
diff --git a/acode.py b/acode.py
index 856cb09..48b76a7 100644
--- a/acode.py
+++ b/acode.py
@@ -46,6 +46,8 @@
# ----------------------------
DEFAULT_FLAT_STEP_MM = 1.0
DEFAULT_EPSILON_MM = 0.25
+DEFAULT_ARC_SEG_MM = 0.0
+DEFAULT_ARC_SEG_DEG = 0.0
# ----------------------------
# Helpers
@@ -351,6 +353,30 @@ def emit_arc_one_shot(out: List[str], radius: float, dtheta: float, feed_arc: in
dr = (radius * dtheta) + (TURN_GAIN * (WHEELBASE_MM / 2.0) * dtheta)
emit_w(out, steps_from_mm(dl), steps_from_mm(dr), feed_arc)
+def emit_arc_segmented(
+ out: List[str],
+ radius: float,
+ dtheta: float,
+ feed_arc: int,
+ arc_seg_mm: float,
+ arc_seg_deg: float,
+):
+ arc_len = abs(radius * dtheta)
+
+ n_mm = 1
+ if arc_seg_mm > 1e-9 and arc_len > 1e-9:
+ n_mm = max(1, int(math.ceil(arc_len / arc_seg_mm)))
+
+ n_deg = 1
+ if arc_seg_deg > 1e-9:
+ n_deg = max(1, int(math.ceil(abs(math.degrees(dtheta)) / arc_seg_deg)))
+
+ n = max(1, n_mm, n_deg)
+ step_theta = dtheta / n
+
+ for _ in range(n):
+ emit_arc_one_shot(out, radius, step_theta, feed_arc)
+
def tangent_heading_for_arc(a0: float, dtheta: float) -> float:
ccw = dtheta > 0
return wrap_pi(a0 + (math.pi / 2.0 if ccw else -math.pi / 2.0))
@@ -360,6 +386,8 @@ def primitives_to_acode(
feed_lin: int,
feed_turn: int,
feed_arc: int,
+ arc_seg_mm: float,
+ arc_seg_deg: float,
) -> List[str]:
x = 0.0
y = 0.0
@@ -411,7 +439,14 @@ def go_to_point(nx: float, ny: float):
heading = wrap_pi(heading + dtheta_align)
if abs(prim.dtheta) > 1e-9:
- emit_arc_one_shot(out, prim.radius, prim.dtheta, feed_arc)
+ emit_arc_segmented(
+ out,
+ prim.radius,
+ prim.dtheta,
+ feed_arc,
+ max(0.0, arc_seg_mm),
+ max(0.0, arc_seg_deg),
+ )
ex, ey = prim_end(prim)
x, y = ex, ey
@@ -437,6 +472,8 @@ def main():
ap.add_argument("--flat-step", type=float, default=DEFAULT_FLAT_STEP_MM)
ap.add_argument("--epsilon", type=float, default=DEFAULT_EPSILON_MM)
+ ap.add_argument("--arc-seg-mm", type=float, default=DEFAULT_ARC_SEG_MM, help="maksymalna długość segmentu łuku")
+ ap.add_argument("--arc-seg-deg", type=float, default=DEFAULT_ARC_SEG_DEG, help="maksymalny kąt segmentu łuku")
args = ap.parse_args()
out_path = args.out or (os.path.splitext(args.dxf)[0] + ".acode")
@@ -455,6 +492,8 @@ def main():
feed_lin=args.feed_lin,
feed_turn=args.feed_turn,
feed_arc=args.feed_arc,
+ arc_seg_mm=args.arc_seg_mm,
+ arc_seg_deg=args.arc_seg_deg,
)
with open(out_path, "w", encoding="utf-8") as f:
@@ -467,4 +506,4 @@ def main():
print("turn_gain:", f"{TURN_GAIN:.6f}")
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()
diff --git a/templates/index.html b/templates/index.html
index 9129d9d..aa466a7 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -133,6 +133,7 @@
artNC
+
@@ -232,6 +233,138 @@ artNC
+
+
+
+
+
+
+
+
+ no output
+
+
+
+
Konwersja DXF → ACODE z segmentacją łuków; sterowanie krokowcami z PID po stronie maszyny.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Preview + Symulator (maszyna.png)
+
+
+
![preview]()
+
+
+
+
+
+
+
+
+ Speed
+
+ 2.00x
+
+ Frame
+
+ 0/0
+
+ Sprite scale
+
+ 0.35
+
+ wheelbase
+ -
+
+
+
+
+
+
+
@@ -615,6 +748,7 @@
artNC
requestAnimationFrame(() => {
resizeSim("gen");
resizeSim("text");
+ resizeSim("dxf");
});
};
});
@@ -1066,9 +1200,19 @@ artNC
"simSpriteScaleText", "simSpriteScaleValText"
);
+ const simDxf = makeSim(
+ "dxf",
+ "d_previmg", "simCanvasDxf",
+ "simWheelbaseDxf", "simPosDxf", "simScrubDxf",
+ "simPlayDxf", "simPauseDxf", "simResetDxf",
+ "simSpeedDxf", "simSpeedValDxf",
+ "simSpriteScaleDxf", "simSpriteScaleValDxf"
+ );
+
function resizeSim(which) {
if (which === "gen") simGen.resize();
if (which === "text") simText.resize();
+ if (which === "dxf") simDxf.resize();
}
// ----------------------------
@@ -1262,6 +1406,66 @@ artNC
};
loadFonts();
+
+ // ----------------------------
+ // DXF -> ACODE
+ // ----------------------------
+ let lastDxfId = "";
+
+ async function convertDxf() {
+ qs("dxf_err").textContent = "";
+ const file = qs("dxf").files[0];
+ if (!file) throw new Error("select a DXF first");
+
+ const fd = new FormData();
+ fd.set("dxf", file);
+ fd.set("feed_lin", formVal("d_feed_lin"));
+ fd.set("feed_turn", formVal("d_feed_turn"));
+ fd.set("feed_arc", formVal("d_feed_arc"));
+ fd.set("flat_step", formVal("d_flat_step"));
+ fd.set("epsilon", formVal("d_epsilon"));
+ fd.set("arc_seg_mm", formVal("d_arc_seg_mm"));
+ fd.set("arc_seg_deg", formVal("d_arc_seg_deg"));
+
+ fd.set("viz_dpi", formVal("d_viz_dpi"));
+ fd.set("viz_arc_step_mm", formVal("d_viz_arc_step_mm"));
+ fd.set("viz_arc_step_deg", formVal("d_viz_arc_step_deg"));
+ if (qs("d_viz_equal").checked) fd.set("viz_equal", "1");
+ if (qs("d_viz_invert_y").checked) fd.set("viz_invert_y", "1");
+ if (qs("d_viz_home_resets_pose").checked) fd.set("viz_home_resets_pose", "1");
+
+ const out = await post("/api/dxf_to_acode", fd);
+ lastDxfId = out.gen_id;
+
+ qs("dxf_info").textContent = "gen_id: " + out.gen_id + " lines: " + out.acode_lines;
+ qs("d_previmg").src = "/preview/" + out.gen_id + ".png?ts=" + Date.now();
+ await simDxf.loadData(out.gen_id);
+ }
+
+ qs("dxf_gen").onclick = () => convertDxf().catch(e => qs("dxf_err").textContent = e.message);
+
+ qs("dxf_push").onclick = async () => {
+ qs("dxf_err").textContent = "";
+ if (!lastDxfId) throw new Error("generate first");
+ const fd = new FormData();
+ fd.set("host", qs("host").value.trim());
+ fd.set("port", qs("port").value.trim());
+ const out = await post("/api/push_to_sender", fd);
+
+ qs("acode").value = "loaded from dxf (" + out.lines + " lines)\n";
+ qs("loaded").textContent = "loaded lines: " + out.lines;
+
+ document.querySelector('.tab[data-tab="sender"]').click();
+ };
+
+ qs("dxf_download").onclick = () => {
+ qs("dxf_err").textContent = "";
+ if (!lastDxfId) {
+ qs("dxf_err").textContent = "generate first";
+ return;
+ }
+ window.location.href = "/download/" + lastDxfId + ".acode";
+ };