diff --git a/UVtools/Readme.md b/UVtools/Readme.md deleted file mode 100644 index 3fe8faa..0000000 --- a/UVtools/Readme.md +++ /dev/null @@ -1,192 +0,0 @@ -# UVtools PCB Exposure CLI — Build & Usage Guide - -This guide covers building a wrapper for the`UVtoolsCmd`, for headless Gerber → `.pm4n` (or any slicer format) conversion on Ubuntu. - -> **Note:** The UVtools maintainer is implementing an official equivalent via -> the generic `run` command and a new `FileArray` reflection property -> (`-p FileArray=file.gbr`). Once that ships in a release -> ([issue 1127](https://github.com/sn4k3/UVtools/issues/1127)), you can switch to -> `pcb-expose-official.sh` and skip the patch/compile steps entirely — see -> [Option B](#option-b-official-release-no-compiling-required) below. - ---- - -## Option A: Patched local build (`pcb-expose` command) - -### 1. Install .NET SDK - -```bash -sudo snap install dotnet -export DOTNET_ROOT=/var/snap/dotnet/common/dotnet -dotnet --version -``` - -Add the `export` line to your `~/.bashrc` so it persists across shells: - -```bash -echo 'export DOTNET_ROOT=/var/snap/dotnet/common/dotnet' >> ~/.bashrc -``` - -### 2. Clone the repo - -```bash -cd ~/proj -git clone --depth 1 https://github.com/sn4k3/UVtools.git -cd UVtools -``` - -### 3. Apply the patch - -Copy `pcb-expose.patch` into the repo root, then: - -```bash -git apply ../kicad2msla/UVtools/pcb-expose.patch -``` - -This does two things: -- Adds `UVtools.Cmd/Symbols/PcbExposeCommand.cs` (new `pcb-expose` subcommand) -- Registers it in `UVtools.Cmd/Program.cs` - -Verify it applied cleanly: - -```bash -git add . -git status -# modified: UVtools.Cmd/Program.cs -# new file: UVtools.Cmd/Symbols/PcbExposeCommand.cs -``` - -### 4. Restore & build - -```bash -dotnet restore - -dotnet publish UVtools.Cmd/UVtools.Cmd.csproj \ - -c Release \ - -r linux-x64 \ - --self-contained false \ - -o ./publish/ -``` - -### 5. Copy the OpenCV native library - -The published output doesn't include `libcvextern.so` — copy it from the repo: - -```bash -cp build/platforms/linux-x64/libcvextern.so ./publish/ -``` - -### 6. Test it - -```bash -./publish/UVtoolsCmd pcb-expose \ - ~/proj/kicad2msla/Dummy.pm4n \ - ~/proj/kicad2msla/gerbers/Flow_Controller_Panel-Front.gtl \ - --invert \ - --mirror \ - --exposure 120 \ - --output ~/proj/kicad2msla/Flow_Controller_Panel-Front.pm4n -``` - -### 7. Install the wrapper - -Copy `pcb-expose-patched.sh` somewhere on your `$PATH`, e.g.: - -```bash -# Copy the script -cp ~/proj/kicad2msla/UVtools/pcb-expose-patched.sh ~/.local/bin -# Make it executable -chmod +x ~/.local/bin/pcb-expose-patched.sh -# Create a symlink -ln -s ~/.local/bin/pcb-expose-patched.sh ~/.local/bin/pcb-expose.sh -``` - -By default the wrapper looks for the build at `~/proj/UVtools/publish/UVtoolsCmd`. -If yours is elsewhere, either edit the `UVTOOLSCMD` variable at the top of the -script, or set it as an environment variable each time: - -```bash -export UVTOOLSCMD=~/proj/UVtools/publish/UVtoolsCmd -``` - -### 8. Usage - -```bash -pcb-expose.sh ~/proj/kicad2msla/Dummy.pm4n \ - ~/proj/kicad2msla/gerbers/Flow_Controller_Panel-Front.gtl \ - --invert --mirror --exposure 120 \ - --output ~/proj/kicad2msla/Flow_Controller_Panel-Front.pm4n -``` - -Multiple files / per-file polarity inversion / merging into one layer: - -```bash -pcb-expose.sh Dummy.pm4n \ - Board-F.Cu.gtl Board-B.Cu.gtl \ - --merge --invert-polarity --mirror --exposure 120 \ - --output Board-combined.pm4n -``` - ---- - -## Option B: Official release (no compiling required) - -Once the maintainer's `FileArray` property ships in an official UVtools -release: - -### 1. Download & install the release - -Grab the Linux release from the -[UVtools releases page](https://github.com/sn4k3/UVtools/releases) and -extract it, e.g. to `~/UVtools/`. `libcvextern.so` is bundled in official -releases, so no extra steps are needed there. - -### 2. Install the wrapper - -```bash -# Copy the script -cp ~/proj/kicad2msla/UVtools/pcb-expose-official.sh ~/.local/bin -# Make it executable -chmod +x ~/.local/bin/pcb-expose-official.sh -# Create a symlink -ln -s ~/.local/bin/pcb-expose-official.sh ~/.local/bin/pcb-expose.sh -``` - -By default it looks for `~/UVtools/UVtoolsCmd`. Override with: - -```bash -export UVTOOLSCMD=~/UVtools/UVtoolsCmd -``` - -### 3. Usage - -Same interface as Option A (minus `--invert-polarity`, which the official -`FileArray` property doesn't support per-file): - -```bash -pcb-expose.sh ~/proj/kicad2msla/Dummy.pm4n \ - ~/proj/kicad2msla/gerbers/Flow_Controller_Panel-Front.gtl \ - --invert --mirror --exposure 120 \ - --output ~/proj/kicad2msla/Flow_Controller_Panel-Front.pm4n -``` - -Internally this runs: - -```bash -UVtoolsCmd run Dummy.pm4n PCBExposure \ - -p FileArray=Board-F.Cu.gtl \ - -p InvertColor=true \ - -p Mirror=true \ - -p ExposureTime=120 \ - --output Board-F.Cu.pm4n -``` - ---- - -## Files in this bundle - -| File | Purpose | -|---|---| -| `pcb-expose.patch` | `git apply`-able patch adding the `pcb-expose` command (Option A) | -| `pcb-expose-patched.sh` | Wrapper for the patched local build | -| `pcb-expose-official.sh` | Wrapper for the official release using `run` + `FileArray` | diff --git a/UVtools/pcb-expose-official.sh b/UVtools/pcb-expose-official.sh deleted file mode 100644 index 49066d4..0000000 --- a/UVtools/pcb-expose-official.sh +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env bash -# -# Wrapper for the OFFICIAL UVtoolsCmd release, using the generic `run` command -# with the new FileArray-capable PCBExposure operation. -# -# Mirrors the interface of pcb-expose-patched.sh, but maps everything onto: -# -# UVtoolsCmd run PCBExposure \ -# -p FileArray= -p FileArray= ... \ -# -p InvertColor=true -p Mirror=true -p ExposureTime=120 ... \ -# --output -# -# Usage: -# pcb-expose-official.sh [options] -# -# Example: -# pcb-expose-official.sh Dummy.pm4n \ -# gerbers/Flow_Controller_Panel-Front.gtl \ -# --invert --mirror --exposure 120 \ -# --output Flow_Controller_Panel-Front.pm4n -# -# Options: -# -e, --exposure Sets ExposureTime -# -i, --invert Sets InvertColor=true -# -m, --mirror Sets Mirror=true -# --merge Sets MergeFiles=true -# --aa Sets EnableAntiAliasing=true -# --offset-x Sets OffsetX -# --offset-y Sets OffsetY -# -o, --output Output file (default: overwrite dummy file) -# -# NOTE: --invert-polarity (per-file InvertPolarity) is NOT supported by this -# wrapper, because the official FileArray property only accepts plain -# file paths, not the InvertPolarity flag per file. If you need that, -# use pcb-expose-patched.sh instead. -# -# Configure the path to your official build below, or override via: -# UVTOOLSCMD=/path/to/UVtoolsCmd pcb-expose-official.sh ... - -set -euo pipefail - -UVTOOLSCMD="${UVTOOLSCMD:-$HOME/UVtools/UVtoolsCmd}" - -if [ ! -x "$UVTOOLSCMD" ]; then - echo "Error: UVtoolsCmd not found or not executable at: $UVTOOLSCMD" >&2 - echo "Set UVTOOLSCMD=/path/to/UVtoolsCmd or edit this script." >&2 - exit 1 -fi - -if [ $# -lt 2 ]; then - echo "Usage: $0 [options]" >&2 - exit 1 -fi - -DUMMY_FILE="$1" -shift - -GERBERS=() -PROPS=() # FileArray entries + scalar properties, built below -OUTPUT_FILE="" - -EXPOSURE="" -INVERT="false" -MIRROR="false" -MERGE="false" -AA="false" -OFFSET_X="" -OFFSET_Y="" -INVERT_POLARITY="false" - -while [ $# -gt 0 ]; do - case "$1" in - -e|--exposure) - EXPOSURE="$2"; shift 2 ;; - -i|--invert) - INVERT="true"; shift ;; - -m|--mirror) - MIRROR="true"; shift ;; - --merge) - MERGE="true"; shift ;; - --aa) - AA="true"; shift ;; - --offset-x) - OFFSET_X="$2"; shift 2 ;; - --offset-y) - OFFSET_Y="$2"; shift 2 ;; - --invert-polarity) - INVERT_POLARITY="true"; shift ;; - -o|--output) - OUTPUT_FILE="$2"; shift 2 ;; - -*) - echo "Unknown option: $1" >&2 - exit 1 ;; - *) - GERBERS+=("$1"); shift ;; - esac -done - -if [ "$INVERT_POLARITY" = "true" ]; then - echo "Warning: --invert-polarity is not supported via the official FileArray" >&2 - echo " property and will be ignored. Use pcb-expose-patched.sh instead." >&2 -fi - -if [ ${#GERBERS[@]} -eq 0 ]; then - echo "Error: specify at least one gerber file." >&2 - exit 1 -fi - -for g in "${GERBERS[@]}"; do - if [ ! -f "$g" ]; then - echo "Error: gerber file not found: $g" >&2 - exit 1 - fi -done - -# Build -p FileArray=... entries -for g in "${GERBERS[@]}"; do - PROPS+=(-p "FileArray=$g") -done - -[ -n "$EXPOSURE" ] && PROPS+=(-p "ExposureTime=$EXPOSURE") -[ "$INVERT" = "true" ] && PROPS+=(-p "InvertColor=true") -[ "$MIRROR" = "true" ] && PROPS+=(-p "Mirror=true") -[ "$MERGE" = "true" ] && PROPS+=(-p "MergeFiles=true") -[ "$AA" = "true" ] && PROPS+=(-p "EnableAntiAliasing=true") -[ -n "$OFFSET_X" ] && PROPS+=(-p "OffsetX=$OFFSET_X") -[ -n "$OFFSET_Y" ] && PROPS+=(-p "OffsetY=$OFFSET_Y") - -CMD=("$UVTOOLSCMD" run "$DUMMY_FILE" PCBExposure "${PROPS[@]}") -[ -n "$OUTPUT_FILE" ] && CMD+=(--output "$OUTPUT_FILE") - -echo "+ ${CMD[*]}" >&2 -exec "${CMD[@]}" diff --git a/UVtools/pcb-expose-patched.sh b/UVtools/pcb-expose-patched.sh deleted file mode 100644 index bf37ecc..0000000 --- a/UVtools/pcb-expose-patched.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -# -# Wrapper for a locally patched UVtoolsCmd build that provides the -# native `pcb-expose` command (see pcb-expose.patch). -# -# Usage: -# pcb-expose-patched.sh [options] -# -# Example: -# pcb-expose-patched.sh Dummy.pm4n \ -# Board-F.Cu.gtl \ -# --invert --mirror --exposure 120 \ -# --output Board-F.Cu.pm4n -# -# Configure the path to your patched build below, or override via: -# UVTOOLSCMD=/path/to/UVtoolsCmd pcb-expose-patched.sh ... - -set -euo pipefail - -UVTOOLSCMD="${UVTOOLSCMD:-$HOME/proj/UVtools/publish/UVtoolsCmd}" - -if [ ! -x "$UVTOOLSCMD" ]; then - echo "Error: UVtoolsCmd not found or not executable at: $UVTOOLSCMD" >&2 - echo "Set UVTOOLSCMD=/path/to/UVtoolsCmd or edit this script." >&2 - exit 1 -fi - -exec "$UVTOOLSCMD" pcb-expose "$@" diff --git a/UVtools/pcb-expose.patch b/UVtools/pcb-expose.patch deleted file mode 100644 index 278092a..0000000 --- a/UVtools/pcb-expose.patch +++ /dev/null @@ -1,173 +0,0 @@ -diff --git a/UVtools.Cmd/Program.cs b/UVtools.Cmd/Program.cs -index 6d2d1ff..38da048 100644 ---- a/UVtools.Cmd/Program.cs -+++ b/UVtools.Cmd/Program.cs -@@ -45,6 +45,7 @@ public static async Task Main(params string[] args) - ExtractCommand.CreateCommand(), - CopyParametersCommand.CreateCommand(), - SetThumbnailCommand.CreateCommand(), -+ PcbExposeCommand.CreateCommand(), - - CompareCommand.CreateCommand(), - -diff --git a/UVtools.Cmd/Symbols/PcbExposeCommand.cs b/UVtools.Cmd/Symbols/PcbExposeCommand.cs -new file mode 100644 -index 0000000..e67e876 ---- /dev/null -+++ b/UVtools.Cmd/Symbols/PcbExposeCommand.cs -@@ -0,0 +1,155 @@ -+/* -+ * GNU AFFERO GENERAL PUBLIC LICENSE -+ * Version 3, 19 November 2007 -+ * Copyright (C) 2007 Free Software Foundation, Inc. -+ * Everyone is permitted to copy and distribute verbatim copies -+ * of this license document, but changing it is not allowed. -+ */ -+ -+using System; -+using System.CommandLine; -+using System.IO; -+using UVtools.Core.FileFormats; -+using UVtools.Core.Operations; -+ -+namespace UVtools.Cmd.Symbols; -+ -+internal static class PcbExposeCommand -+{ -+ internal static Command CreateCommand() -+ { -+ var gerberArgument = new Argument("gerber-files") -+ { -+ Description = "One or more Gerber/drill files (.gbr .gtl .gbl .gts .gbs .drl etc.)", -+ }; -+ -+ var exposureOption = new Option("-e", "--exposure") -+ { -+ Description = "Exposure time in seconds (0 = keep value from input file)", -+ DefaultValueFactory = _ => 0m, -+ }; -+ -+ var invertOption = new Option("-i", "--invert") -+ { -+ Description = "Invert image color", -+ }; -+ -+ var mirrorOption = new Option("-m", "--mirror") -+ { -+ Description = "Mirror the image horizontally", -+ }; -+ -+ var mergeOption = new Option("--merge") -+ { -+ Description = "Merge all gerber files into a single layer", -+ }; -+ -+ var invertPolarityOption = new Option("--invert-polarity") -+ { -+ Description = "Invert Gerber drawing polarity for all supplied files", -+ }; -+ -+ var antiAliasingOption = new Option("--aa") -+ { -+ Description = "Enable anti-aliasing", -+ }; -+ -+ var offsetXOption = new Option("--offset-x") -+ { -+ Description = "X offset in mm", -+ DefaultValueFactory = _ => 0m, -+ }; -+ -+ var offsetYOption = new Option("--offset-y") -+ { -+ Description = "Y offset in mm", -+ DefaultValueFactory = _ => 0m, -+ }; -+ -+ var command = new Command("pcb-expose", "Inject a Gerber file into a slicer file for PCB UV exposure") -+ { -+ GlobalArguments.InputFileArgument, -+ gerberArgument, -+ -+ exposureOption, -+ invertOption, -+ mirrorOption, -+ mergeOption, -+ invertPolarityOption, -+ antiAliasingOption, -+ offsetXOption, -+ offsetYOption, -+ GlobalOptions.OutputFile, -+ }; -+ -+ command.SetAction(result => -+ { -+ var inputFile = result.GetRequiredValue(GlobalArguments.InputFileArgument); -+ var gerbers = result.GetRequiredValue(gerberArgument); -+ var exposure = result.GetValue(exposureOption); -+ var invert = result.GetValue(invertOption); -+ var mirror = result.GetValue(mirrorOption); -+ var merge = result.GetValue(mergeOption); -+ var invertPolarity = result.GetValue(invertPolarityOption); -+ var aa = result.GetValue(antiAliasingOption); -+ var offsetX = result.GetValue(offsetXOption); -+ var offsetY = result.GetValue(offsetYOption); -+ var outputFile = result.GetValue(GlobalOptions.OutputFile); -+ -+ if (gerbers.Length == 0) -+ { -+ Program.WriteLineError("Specify at least one gerber file."); -+ return; -+ } -+ -+ foreach (var g in gerbers) -+ { -+ if (!g.Exists) -+ { -+ Program.WriteLineError($"Gerber file not found: {g.FullName}"); -+ return; -+ } -+ } -+ -+ var slicerFile = Program.OpenInputFile(inputFile); -+ -+ var op = new OperationPCBExposure(slicerFile) -+ { -+ MergeFiles = merge, -+ Mirror = mirror, -+ InvertColor = invert, -+ EnableAntiAliasing = aa, -+ OffsetX = offsetX, -+ OffsetY = offsetY, -+ }; -+ -+ if (exposure > 0) -+ op.ExposureTime = exposure; -+ -+ foreach (var g in gerbers) -+ op.Files.Add(new OperationPCBExposure.PCBExposureFile(g.FullName, invertPolarity)); -+ -+ var spawnError = op.ValidateSpawn(); -+ if (!string.IsNullOrWhiteSpace(spawnError)) -+ { -+ Program.WriteLineError(spawnError); -+ return; -+ } -+ -+ var validationError = op.ValidateInternally(); -+ if (!string.IsNullOrWhiteSpace(validationError)) -+ { -+ Program.WriteLineError(validationError.TrimEnd()); -+ return; -+ } -+ -+ Program.ProgressBarWork( -+ $"PCB exposure: {string.Join(", ", Array.ConvertAll(gerbers, g => g.Name))}", -+ () => op.Execute(Program.Progress)); -+ -+ Program.SaveFile(slicerFile, outputFile); -+ }); -+ -+ return command; -+ } -+}