Compare commits
17 Commits
5c8ad98bb5
...
5be67afa54
| Author | SHA1 | Date |
|---|---|---|
|
|
5be67afa54 | |
|
|
3970f9bca7 | |
|
|
f46000649c | |
|
|
9c819d6325 | |
|
|
a23e71ab5a | |
|
|
ce836a568c | |
|
|
23d7c0d52f | |
|
|
129f8ab159 | |
|
|
4471fe0bb9 | |
|
|
8d699bccd6 | |
|
|
a99ca6431e | |
|
|
d5fd5b8c38 | |
|
|
979d769bfb | |
|
|
08ece46452 | |
|
|
4c78b77e60 | |
|
|
d3b21fb2f1 | |
|
|
ce12e90225 |
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "1/8th Drill Bit",
|
||||
"shape": "drill.fcstd",
|
||||
"parameter": {
|
||||
"Chipload": "0.000 in",
|
||||
"Diameter": "0.125 in",
|
||||
"Flutes": "0",
|
||||
"Length": "0.750 in",
|
||||
"Material": "HSS",
|
||||
"TipAngle": "135.000 \u00b0"
|
||||
},
|
||||
"attribute": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "3/16 Endmill",
|
||||
"shape": "endmill.fcstd",
|
||||
"parameter": {
|
||||
"Chipload": "0.000 in",
|
||||
"CuttingEdgeHeight": "0.600 \"",
|
||||
"Diameter": "0.187 \"",
|
||||
"Flutes": "0",
|
||||
"Length": "1.500 \"",
|
||||
"Material": "HSS",
|
||||
"ShankDiameter": "0.315 \"",
|
||||
"SpindleDirection": "Forward"
|
||||
},
|
||||
"attribute": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "45 Deg. Chamfer",
|
||||
"shape": "chamfer.fcstd",
|
||||
"parameter": {
|
||||
"CuttingEdgeAngle": "45.0000 \u00b0",
|
||||
"CuttingEdgeHeight": "6.3500 mm",
|
||||
"Diameter": "12.3323 mm",
|
||||
"Length": "30.0000 mm",
|
||||
"ShankDiameter": "6.3500 mm",
|
||||
"TipDiameter": "5.0000 mm"
|
||||
},
|
||||
"attribute": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "5mm-thread-cutter",
|
||||
"shape": "thread-mill.fcstd",
|
||||
"parameter": {
|
||||
"Crest": "0.10 mm",
|
||||
"Diameter": "5.00 mm",
|
||||
"Length": "50.00 mm",
|
||||
"NeckDiameter": "3.00 mm",
|
||||
"NeckLength": "20.00 mm",
|
||||
"ShankDiameter": "5.00 mm"
|
||||
},
|
||||
"attribute": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "5mm Drill",
|
||||
"shape": "drill.fcstd",
|
||||
"parameter": {
|
||||
"Diameter": "5.0000 mm",
|
||||
"Length": "50.0000 mm",
|
||||
"TipAngle": "119.0000 \u00b0"
|
||||
},
|
||||
"attribute": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "5mm Endmill",
|
||||
"shape": "endmill.fcstd",
|
||||
"parameter": {
|
||||
"CuttingEdgeHeight": "30.0000 mm",
|
||||
"Diameter": "5.0000 mm",
|
||||
"Length": "50.0000 mm",
|
||||
"ShankDiameter": "3.0000 mm"
|
||||
},
|
||||
"attribute": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "60 Deg. V-Bit",
|
||||
"shape": "v-bit.fcstd",
|
||||
"parameter": {
|
||||
"CuttingEdgeAngle": "60.0000 \u00b0",
|
||||
"Diameter": "10.0000 mm",
|
||||
"CuttingEdgeHeight": "1.0000 mm",
|
||||
"TipDiameter": "1.0000 mm",
|
||||
"Length": "20.0000 mm",
|
||||
"ShankDiameter": "5.0000 mm"
|
||||
},
|
||||
"attribute": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "6mm Ball End",
|
||||
"shape": "ballend.fcstd",
|
||||
"parameter": {
|
||||
"CuttingEdgeHeight": "40.0000 mm",
|
||||
"Diameter": "6.0000 mm",
|
||||
"Length": "50.0000 mm",
|
||||
"ShankDiameter": "3.0000 mm"
|
||||
},
|
||||
"attribute": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "6 mm Bull Nose",
|
||||
"shape": "bullnose.fcstd",
|
||||
"parameter": {
|
||||
"CuttingEdgeHeight": "40.0000 mm",
|
||||
"Diameter": "6.0000 mm",
|
||||
"FlatRadius": "1.5000 mm",
|
||||
"Length": "50.0000 mm",
|
||||
"ShankDiameter": "3.0000 mm"
|
||||
},
|
||||
"attribute": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Probe",
|
||||
"shape": "probe.fcstd",
|
||||
"parameter": {
|
||||
"Diameter": "6.0000 mm",
|
||||
"Length": "50.0000 mm",
|
||||
"ShaftDiameter": "4.0000 mm"
|
||||
},
|
||||
"attribute": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Single Flute 1/8th inch endmill",
|
||||
"shape": "endmill.fcstd",
|
||||
"parameter": {
|
||||
"Chipload": "0.000 in",
|
||||
"CuttingEdgeHeight": "0.600 \"",
|
||||
"Diameter": "0.125 \"",
|
||||
"Flutes": "0",
|
||||
"Length": "1.000 \"",
|
||||
"Material": "HSS",
|
||||
"ShankDiameter": "0.315 \"",
|
||||
"SpindleDirection": "Forward"
|
||||
},
|
||||
"attribute": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"version": 2,
|
||||
"name": "Slitting Saw",
|
||||
"shape": "slittingsaw.fcstd",
|
||||
"parameter": {
|
||||
"BladeThickness": "3.0000 mm",
|
||||
"CapHeight": "3.0000 mm",
|
||||
"CapDiameter": "8.0000 mm",
|
||||
"Diameter": "76.2000 mm",
|
||||
"Length": "50.0000 mm",
|
||||
"ShankDiameter": "19.0500 mm"
|
||||
},
|
||||
"attribute": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"tools": [
|
||||
{
|
||||
"nr": 1,
|
||||
"path": "5mm_Endmill.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 2,
|
||||
"path": "5mm_Drill.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 3,
|
||||
"path": "6mm_Ball_End.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 4,
|
||||
"path": "6mm_Bullnose.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 5,
|
||||
"path": "60degree_Vbit.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 6,
|
||||
"path": "45degree_chamfer.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 7,
|
||||
"path": "slittingsaw.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 8,
|
||||
"path": "probe.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 9,
|
||||
"path": "5mm-thread-cutter.fctb"
|
||||
}
|
||||
],
|
||||
"version": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"tools": [
|
||||
{
|
||||
"nr": 1,
|
||||
"path": "125_Drill.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 2,
|
||||
"path": "singleFlute8th_Endmill.fctb"
|
||||
}
|
||||
],
|
||||
"version": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
# Tools
|
||||
|
||||
Each tool is stored as a JSON file which has the shape's path and values for all attributes of the shape.
|
||||
It also includes all additional parameters and their values.
|
||||
|
||||
Storing a tool as a JSON file sounds great but eliminates the option of an accurate thumbnail. On the other hand,
|
||||
storing each tool as a `*.fcstd` file requires more space and does not allow for generating tools. If one has an
|
||||
extensive tool aresenal they might want to script the generation of tools which is easily done for a `*.json` file but
|
||||
practically impossible for `*.fcstd` files.
|
||||
|
||||
When a tool is instantiated in a job the PDN body is created from the shape and the attributes and constraints are set
|
||||
according to the values from the JSON file. All additional parameters are created as properties on the object. This
|
||||
provides the correct shape and dimensions which can be used to generate a point cloud or mesh for advanced
|
||||
algorithms (and potentially simulation).
|
||||
|
||||
# Tool Libraries
|
||||
|
||||
Due to each tool being stored in its own file and the storage/organization of those files being quite flexible the
|
||||
importance of a tool library for organisational purposes is quite diminished. The user is free to organise their tools
|
||||
in whichever directory hierarchy they see fit and can also name them as best fits their use and organisation. A
|
||||
_tool library_ is nevertheless a great representation for a physical grouping of tools, such as in an automatic tool
|
||||
changer.
|
||||
|
||||
A tool library is a (JSON) file with a mapping of tool id to the path of the tool file. As a consequence each tool
|
||||
can be in multiple libraries and doesn't have an `id` of it's own. The `id` is a property of the library.
|
||||
|
||||
If a tool from a tool library (or an entire tool library) is added to a job it retains its `id` from the library as a
|
||||
property. Adding a tool bit directly rsults in the tool getting the next free id assigned.
|
||||
|
||||
# Tool Controllers
|
||||
|
||||
They largely stay the same as they are today. As an additional feature it should be possible to _copy_ a TC, which
|
||||
allows for easy feed/speed changes for the same tool.
|
||||
|
||||
Above requirement highlights one change though, that the `id` should be a property of the Bit, and not of the TC.
|
||||
There are two requirements that are currently mapped to a single `id`. There needs to be an identification of which
|
||||
TC is being used by a certain op, and which tool number to use for a `M6` command.
|
||||
|
||||
# Paths and Extensibility
|
||||
|
||||
The following directory structure is used for supplied (shipped with FreeCAD) tools:
|
||||
```
|
||||
Tools
|
||||
+ Bit
|
||||
+ Library
|
||||
+ Shape
|
||||
```
|
||||
|
||||
Strictly speaking a user is free to store their tools wherever they want and however they want. By default the file
|
||||
dialog will open the corresponding directory (depending on context), or whichever directory the user opened last.
|
||||
|
||||
Above directory structure with the most common default tools shipped with FreeCAD should be installed analogous to
|
||||
TechDraw's templates.
|
||||
|
||||
## How to create a new tool
|
||||
|
||||
1. Set the tool's Label, this will show up in the object tree
|
||||
1. Select a tool shape from the existing shape files. If your tool doesn't exist, you'll have to create a new shape,
|
||||
see below for details.
|
||||
1. Each tool bit shape has its own set of parameters, fill them with the tool's values.
|
||||
1. Select additional parameters
|
||||
1. Save the tool under path/file that makes sense to you
|
||||
|
||||
|
||||
## How to create a new tool bit Shape
|
||||
|
||||
The shape file for a tool bit is expected to contain a PD body which represents the tool as a 3d solid. The PD body
|
||||
should be parametric based on a PropertyBag object so that, when the properties of the PropertyBag are changed the
|
||||
solid is updated to the correct representation.
|
||||
|
||||
1. Create a new FreeCAD document
|
||||
1. Open the `PartDesign` workbench, create a body and give the body a label you want to show up in the bit selection.
|
||||
1. Open the Path workbench and (with the PD body selected) create a PropertyBag,
|
||||
menu 'Path' -> 'Utils' -> 'Property Bag'
|
||||
* this creates a PropertyBag object inside the Body (assuming it was selected)
|
||||
* add properties to which define the tool bit's shape and put those into the group 'Shape'
|
||||
* add any other properties to the bag which might be useful for the tool bit
|
||||
1. Construct the body of the tool bit and assign expressions referencing properties from the PropertyBag (in the
|
||||
`Shape` Group) for all constraints.
|
||||
* Position the tip of the tool bit on the origin (0,0)
|
||||
1. Save the document as a new file in the Shape directory
|
||||
* Before saving the document make sure you have _Save Thumbnail_ selected, and _Add program logo_ deselected in
|
||||
FreeCAD's preferences.
|
||||
* Also make sure to switch to _Front View_ and _Fit content to screen_
|
||||
* Whatever you see when saving the document will end up being the visual representation of tool bits with this shape
|
||||
|
||||
Not that 'Shape' is the only property group which has special meaning for tool bits. All other property groups are
|
||||
copied verbatim to the ToolBit object when one is created.
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
set number
|
||||
syntax on
|
||||
set expandtab shiftwidth=4 tabstop=4
|
||||
set autoindent
|
||||
set clipboard=unnamedplus
|
||||
|
||||
" I want nvim using a venv for plugins and stuff
|
||||
let g:python3_host_prog = expand('~/.venvs/nvim/bin/python')
|
||||
|
||||
" Plugins with vim-plug
|
||||
call plug#begin('~/.vim/plugged')
|
||||
|
||||
Plug 'psf/black', { 'do': ':!pip install black' }
|
||||
Plug 'dense-analysis/ale' " For linting and format-on-save
|
||||
Plug 'vim-python/python-syntax' " Improved Python syntax highlighting
|
||||
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } " Fuzzy finder
|
||||
Plug 'junegunn/fzf.vim'
|
||||
Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'} " Better syntax
|
||||
|
||||
call plug#end()
|
||||
|
||||
" Enable ALE for linting and formatting
|
||||
let g:ale_fix_on_save = 1
|
||||
let g:ale_linters = { 'python': ['flake8'] }
|
||||
let g:ale_fixers = { 'python': ['black'] }
|
||||
|
||||
" Keybindings
|
||||
nnoremap <leader>f :Files<CR> " Fuzzy file search
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
# LaTeX Import path
|
||||
export TEXINPUTS=/home/joe/College/SNHU/LaTeX:$(kpsepath tex | tr ':' '\n' | head -n 1):
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
[/home/joe/.local/share/plasma-vault/Blackmail.enc]
|
||||
activities=
|
||||
backend=cryfs
|
||||
lastError=Unknown device (code: 1)
|
||||
lastStatus=2
|
||||
mountPoint=/home/joe/Vaults/Blackmail
|
||||
name=Blackmail
|
||||
offlineOnly=false
|
||||
|
||||
[/home/joe/.local/share/plasma-vault/NSFW.enc]
|
||||
activities=
|
||||
backend=cryfs
|
||||
lastError=Cannot create the mount point (code: 0)
|
||||
lastStatus=1
|
||||
mountPoint=/home/joe/Vaults/NSFW
|
||||
name=NSFW
|
||||
offlineOnly=false
|
||||
|
||||
[EncryptedDevices]
|
||||
/home/joe/.local/share/plasma-vault/Blackmail.enc=true
|
||||
/home/joe/.local/share/plasma-vault/NSFW.enc=true
|
||||
|
||||
[UI-notice]
|
||||
SkipNotice-cryfs-message=false
|
||||
13
.gitconfig
13
.gitconfig
|
|
@ -1,5 +1,5 @@
|
|||
[user]
|
||||
email = kenwood364@gmail.com
|
||||
email = joe@kitsunehosting.net
|
||||
name = KenwoodFox
|
||||
signingkey = DD5E787D6C678F3F64
|
||||
[alias]
|
||||
|
|
@ -23,3 +23,14 @@
|
|||
autosetuprebase = always
|
||||
[safe]
|
||||
directory = /opt/flutter
|
||||
[protocol "file"]
|
||||
allow = always
|
||||
[filter "lfs"]
|
||||
required = true
|
||||
clean = git-lfs clean -- %f
|
||||
smudge = git-lfs smudge -- %f
|
||||
process = git-lfs filter-process
|
||||
[diff "fcinfo"]
|
||||
textconv = ~/.local/bin/fcinfo
|
||||
[core]
|
||||
hooksPath = /home/joe/.githooks
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
#!/bin/sh
|
||||
# Global pre-commit hook: print effective identity and ask to continue.
|
||||
# Set BYPASS_COMMIT_IDENTITY_CHECK=1 to skip (e.g., in CI).
|
||||
|
||||
[ -n "$BYPASS_COMMIT_IDENTITY_CHECK" ] && exit 0
|
||||
|
||||
# Resolve effective identity (prefer repo-local config, fall back to global)
|
||||
name="$(git config --get user.name)"
|
||||
[ -z "$name" ] && name="$(git config --global --get user.name)"
|
||||
email="$(git config --get user.email)"
|
||||
[ -z "$email" ] && email="$(git config --global --get user.email)"
|
||||
|
||||
branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo '?')"
|
||||
remote="$(git remote -v | awk '/\(push\)/{print $2; exit}' | sed 's#^https\?://##; s#git@##' )"
|
||||
|
||||
# Optional: block specific identities unless overridden
|
||||
# Add your alt(s) here, comma-separated (exact match on email or name).
|
||||
BLOCKED_EMAILS="${BLOCKED_EMAILS:-alt@example.com}"
|
||||
BLOCKED_NAMES="${BLOCKED_NAMES:-Alt Name}"
|
||||
|
||||
is_blocked() {
|
||||
needle="$1" haystack="$2"
|
||||
printf '%s' "$haystack" | tr ',' '\n' | grep -Fxq "$needle"
|
||||
}
|
||||
|
||||
printf '\n\033[1;33m⚠ Committing as:\033[0m \033[1m%s\033[0m <\033[1m%s\033[0m>\n' "$name" "$email"
|
||||
[ -n "$branch" ] && printf ' Branch: %s\n' "$branch"
|
||||
[ -n "$remote" ] && printf ' Remote: %s\n' "$remote"
|
||||
printf ' Double-check this is the right account.\n\n'
|
||||
|
||||
if is_blocked "$email" "$BLOCKED_EMAILS" || is_blocked "$name" "$BLOCKED_NAMES"; then
|
||||
printf '\033[1;31mBlocked identity detected.\033[0m Set BYPASS_COMMIT_IDENTITY_CHECK=1 to override just this commit.\n'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Interactive confirm (skip if no TTY, e.g., merge drivers)
|
||||
if [ -t 0 ]; then
|
||||
printf 'Continue with commit? [y/N] '
|
||||
read ans
|
||||
case "$ans" in
|
||||
y|Y) ;;
|
||||
*) echo "Commit aborted."; exit 1 ;;
|
||||
esac
|
||||
fi
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Re-encode a video to a target size in MB.
|
||||
# Example:
|
||||
# ./this_script.sh video.mp4 15
|
||||
|
||||
T_SIZE="$2" # target size in MB
|
||||
T_FILE="${1%.*}-$2MB.mp4" # filename out
|
||||
|
||||
# Original duration in seconds
|
||||
O_DUR=$(\
|
||||
ffprobe \
|
||||
-v error \
|
||||
-show_entries format=duration \
|
||||
-of csv=p=0 "$1")
|
||||
|
||||
# Original audio rate
|
||||
O_ARATE=$(\
|
||||
ffprobe \
|
||||
-v error \
|
||||
-select_streams a:0 \
|
||||
-show_entries stream=bit_rate \
|
||||
-of csv=p=0 "$1")
|
||||
|
||||
# Original audio rate in KiB/s
|
||||
O_ARATE=$(\
|
||||
awk \
|
||||
-v arate="$O_ARATE" \
|
||||
'BEGIN { printf "%.0f", (arate / 1024) }')
|
||||
|
||||
# Target size is required to be less than the size of the original audio stream
|
||||
T_MINSIZE=$(\
|
||||
awk \
|
||||
-v arate="$O_ARATE" \
|
||||
-v duration="$O_DUR" \
|
||||
'BEGIN { printf "%.2f", ( (arate * duration) / 8192 ) }')
|
||||
|
||||
# Equals 1 if target size is ok, 0 otherwise
|
||||
IS_MINSIZE=$(\
|
||||
awk \
|
||||
-v size="$T_SIZE" \
|
||||
-v minsize="$T_MINSIZE" \
|
||||
'BEGIN { print (minsize < size) }')
|
||||
|
||||
# Give useful information if size is too small
|
||||
if [[ $IS_MINSIZE -eq 0 ]]; then
|
||||
printf "%s\n" "Target size ${T_SIZE}MB is too small!" >&2
|
||||
printf "%s %s\n" "Try values larger than" "${T_MINSIZE}MB" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set target audio bitrate
|
||||
T_ARATE=$O_ARATE
|
||||
|
||||
|
||||
# Calculate target video rate - MB -> KiB/s
|
||||
T_VRATE=$(\
|
||||
awk \
|
||||
-v size="$T_SIZE" \
|
||||
-v duration="$O_DUR" \
|
||||
-v audio_rate="$O_ARATE" \
|
||||
'BEGIN { print ( ( size * 8192.0 ) / ( 1.048576 * duration ) - audio_rate) }')
|
||||
|
||||
# Perform the conversion
|
||||
ffmpeg \
|
||||
-y \
|
||||
-i "$1" \
|
||||
-c:v libx264 \
|
||||
-b:v "$T_VRATE"k \
|
||||
-pass 1 \
|
||||
-an \
|
||||
-f mp4 \
|
||||
/dev/null \
|
||||
&& \
|
||||
ffmpeg \
|
||||
-i "$1" \
|
||||
-c:v libx264 \
|
||||
-b:v "$T_VRATE"k \
|
||||
-pass 2 \
|
||||
-c:a aac \
|
||||
-b:a "$T_ARATE"k \
|
||||
$T_FILE
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
# -*- coding: utf8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2015 Yorik van Havre <yorik@uncreated.net> *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
__title__ = "FreeCAD File info utility"
|
||||
__author__ = "Yorik van Havre"
|
||||
__url__ = ["http://www.freecad.org"]
|
||||
__doc__ = """
|
||||
This utility prints information about a given FreeCAD file (*.FCStd)
|
||||
on screen, including document properties, number of included objects,
|
||||
object sizes and properties and values. Its main use is to compare
|
||||
two files and be able to see the differences in a text-based form.
|
||||
|
||||
If no option is used, fcinfo prints the document properties and a list
|
||||
of properties of each object found in the given file.
|
||||
|
||||
Usage:
|
||||
|
||||
fcinfo [options] myfile.FCStd
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help: Prints this help text
|
||||
-s, --short: Do not print object properties. Only one line
|
||||
per object is printed, including its size and SHA1.
|
||||
This is sufficient to see that an object has
|
||||
changed, but not what exactly has changed.
|
||||
-vs --veryshort: Only prints the document info, not objects info.
|
||||
This is sufficient to see if a file has changed, as
|
||||
its SHA1 code and timestamp will show it. But won't
|
||||
show details of what has changed.
|
||||
-g --gui: Adds visual properties too (if not using -s or -vs)
|
||||
|
||||
Git usage:
|
||||
|
||||
This script can be used as a textconv tool for git diff by
|
||||
configuring your git folder as follows:
|
||||
|
||||
1) add to .gitattributes (or ~/.gitattributes for user-wide):
|
||||
|
||||
*.fcstd diff=fcinfo
|
||||
|
||||
2) add to .git/config (or ~/.gitconfig for user-wide):
|
||||
|
||||
[diff "fcinfo"]
|
||||
textconv = /path/to/fcinfo
|
||||
|
||||
With this, when committing a .FCStd file with Git,
|
||||
'git diff' will show you the difference between the two
|
||||
texts obtained by fcinfo
|
||||
"""
|
||||
|
||||
|
||||
import sys
|
||||
import zipfile
|
||||
import xml.sax
|
||||
import os
|
||||
import hashlib
|
||||
import re
|
||||
|
||||
|
||||
class FreeCADFileHandler(xml.sax.ContentHandler):
|
||||
def __init__(self, zfile, short=0): # short: 0=normal, 1=short, 2=veryshort
|
||||
|
||||
xml.sax.ContentHandler.__init__(self)
|
||||
self.zfile = zfile
|
||||
self.obj = None
|
||||
self.prop = None
|
||||
self.count = "0"
|
||||
self.contents = {}
|
||||
self.short = short
|
||||
|
||||
def startElement(self, tag, attributes):
|
||||
|
||||
if tag == "Document":
|
||||
self.obj = tag
|
||||
self.contents = {}
|
||||
self.contents["ProgramVersion"] = attributes["ProgramVersion"]
|
||||
self.contents["FileVersion"] = attributes["FileVersion"]
|
||||
|
||||
elif tag == "Object":
|
||||
if "name" in attributes:
|
||||
name = self.clean(attributes["name"])
|
||||
self.obj = name
|
||||
if "type" in attributes:
|
||||
self.contents[name] = attributes["type"]
|
||||
|
||||
elif tag == "ViewProvider":
|
||||
if "name" in attributes:
|
||||
self.obj = self.clean(attributes["name"])
|
||||
|
||||
elif tag == "Part":
|
||||
if self.obj:
|
||||
r = self.zfile.read(attributes["file"])
|
||||
s = r.__sizeof__()
|
||||
if s < 1024:
|
||||
s = str(s) + "B"
|
||||
elif s > 1048576:
|
||||
s = str(s / 1048576) + "M"
|
||||
else:
|
||||
s = str(s / 1024) + "K"
|
||||
s += " " + str(hashlib.sha1(r).hexdigest()[:12])
|
||||
self.contents[self.obj] += " (" + s + ")"
|
||||
|
||||
elif tag == "Property":
|
||||
self.prop = None
|
||||
# skip "internal" properties, useless for a diff
|
||||
if attributes["name"] not in [
|
||||
"Symbol",
|
||||
"AttacherType",
|
||||
"MapMode",
|
||||
"MapPathParameter",
|
||||
"MapReversed",
|
||||
"AttachmentOffset",
|
||||
"SelectionStyle",
|
||||
"TightGrid",
|
||||
"GridSize",
|
||||
"GridSnap",
|
||||
"GridStyle",
|
||||
"Lighting",
|
||||
"Deviation",
|
||||
"AngularDeflection",
|
||||
"BoundingBox",
|
||||
"Selectable",
|
||||
"ShowGrid",
|
||||
]:
|
||||
self.prop = attributes["name"]
|
||||
|
||||
elif tag in ["String", "Uuid", "Float", "Integer", "Bool", "Link"]:
|
||||
if self.prop and ("value" in attributes):
|
||||
if self.obj == "Document":
|
||||
self.contents[self.prop] = attributes["value"]
|
||||
elif self.short == 0:
|
||||
if tag == "Float":
|
||||
self.contents[self.obj + "00000000::" + self.prop] = str(
|
||||
float(attributes["value"])
|
||||
)
|
||||
else:
|
||||
self.contents[self.obj + "00000000::" + self.prop] = attributes["value"]
|
||||
|
||||
elif tag in ["PropertyVector"]:
|
||||
if self.prop and self.obj and (self.short == 0):
|
||||
val = (
|
||||
"("
|
||||
+ str(float(attributes["valueX"]))
|
||||
+ ","
|
||||
+ str(float(attributes["valueY"]))
|
||||
+ ","
|
||||
+ str(float(attributes["valueZ"]))
|
||||
+ ")"
|
||||
)
|
||||
self.contents[self.obj + "00000000::" + self.prop] = val
|
||||
|
||||
elif tag in ["PropertyPlacement"]:
|
||||
if self.prop and self.obj and (self.short == 0):
|
||||
val = (
|
||||
"("
|
||||
+ str(float(attributes["Px"]))
|
||||
+ ","
|
||||
+ str(float(attributes["Py"]))
|
||||
+ ","
|
||||
+ str(float(attributes["Pz"]))
|
||||
+ ")"
|
||||
)
|
||||
val += (
|
||||
" ("
|
||||
+ str(round(float(attributes["Q0"]), 4))
|
||||
+ ","
|
||||
+ str(round(float(attributes["Q1"]), 4))
|
||||
+ ","
|
||||
)
|
||||
val += (
|
||||
str(round(float(attributes["Q2"]), 4))
|
||||
+ ","
|
||||
+ str(round(float(attributes["Q3"]), 4))
|
||||
+ ")"
|
||||
)
|
||||
self.contents[self.obj + "00000000::" + self.prop] = val
|
||||
|
||||
elif tag in ["PropertyColor"]:
|
||||
if self.prop and self.obj and (self.short == 0):
|
||||
c = int(attributes["value"])
|
||||
r = float((c >> 24) & 0xFF) / 255.0
|
||||
g = float((c >> 16) & 0xFF) / 255.0
|
||||
b = float((c >> 8) & 0xFF) / 255.0
|
||||
val = str((r, g, b))
|
||||
self.contents[self.obj + "00000000::" + self.prop] = val
|
||||
|
||||
elif tag == "Objects":
|
||||
self.count = attributes["Count"]
|
||||
self.obj = None
|
||||
|
||||
# Print all the contents of the document properties
|
||||
items = self.contents.items()
|
||||
items = sorted(items)
|
||||
for key, value in items:
|
||||
key = self.clean(key)
|
||||
value = self.clean(value)
|
||||
print(" " + key + " : " + value)
|
||||
print(" Objects: (" + self.count + ")")
|
||||
self.contents = {}
|
||||
|
||||
def endElement(self, tag):
|
||||
|
||||
if (tag == "Document") and (self.short != 2):
|
||||
items = self.contents.items()
|
||||
items = sorted(items)
|
||||
for key, value in items:
|
||||
key = self.clean(key)
|
||||
if "00000000::" in key:
|
||||
key = " " + key.split("00000000::")[1]
|
||||
value = self.clean(value)
|
||||
if value:
|
||||
print(" " + key + " : " + value)
|
||||
|
||||
def clean(self, value):
|
||||
|
||||
value = value.strip()
|
||||
return value
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
sys.exit()
|
||||
|
||||
if ("-h" in sys.argv[1:]) or ("--help" in sys.argv[1:]):
|
||||
print(__doc__)
|
||||
sys.exit()
|
||||
|
||||
ext = sys.argv[-1].rsplit(".")[-1].lower()
|
||||
if not ext.startswith("fcstd") and not ext.startswith("fcbak"):
|
||||
print(__doc__)
|
||||
sys.exit()
|
||||
|
||||
if ("-vs" in sys.argv[1:]) or ("--veryshort" in sys.argv[1:]):
|
||||
short = 2
|
||||
elif ("-s" in sys.argv[1:]) or ("--short" in sys.argv[1:]):
|
||||
short = 1
|
||||
else:
|
||||
short = 0
|
||||
|
||||
if ("-g" in sys.argv[1:]) or ("--gui" in sys.argv[1:]):
|
||||
gui = True
|
||||
else:
|
||||
gui = False
|
||||
|
||||
zfile = zipfile.ZipFile(sys.argv[-1])
|
||||
|
||||
if not "Document.xml" in zfile.namelist():
|
||||
sys.exit(1)
|
||||
doc = zfile.read("Document.xml")
|
||||
if gui and "GuiDocument.xml" in zfile.namelist():
|
||||
guidoc = zfile.read("GuiDocument.xml")
|
||||
guidoc = re.sub(b"<\?xml.*?-->", b" ", guidoc, flags=re.MULTILINE | re.DOTALL)
|
||||
# a valid xml doc can have only one root element. So we need to insert
|
||||
# all the contents of the GUiDocument <document> tag into the main one
|
||||
doc = re.sub(b"<\/Document>", b"", doc, flags=re.MULTILINE | re.DOTALL)
|
||||
guidoc = re.sub(b"<Document.*?>", b" ", guidoc, flags=re.MULTILINE | re.DOTALL)
|
||||
doc += guidoc
|
||||
s = os.path.getsize(sys.argv[-1])
|
||||
if s < 1024:
|
||||
s = str(s) + "B"
|
||||
elif s > 1048576:
|
||||
s = str(s / 1048576) + "M"
|
||||
else:
|
||||
s = str(s / 1024) + "K"
|
||||
print("Document: " + sys.argv[-1] + " (" + s + ")")
|
||||
print(" SHA1: " + str(hashlib.sha1(open(sys.argv[-1], "rb").read()).hexdigest()))
|
||||
xml.sax.parseString(doc, FreeCADFileHandler(zfile, short))
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
#!/bin/python
|
||||
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
# Dictionary for symbol conversion to LaTeX format
|
||||
latex_symbols = {
|
||||
"Ω": r"\Omega",
|
||||
"ohm": r"\Omega",
|
||||
"A": r"\text{A}",
|
||||
"V": r"\text{V}",
|
||||
"milliamperes": r"\text{mA}",
|
||||
"*": r"\cdot",
|
||||
"N*m": r"\text{N} \cdot \text{m}",
|
||||
"in*lb": r"\text{in} \cdot \text{lb}",
|
||||
"||": r"\|",
|
||||
"−": r"-",
|
||||
"μ": r"\mu",
|
||||
"−": r"-",
|
||||
}
|
||||
|
||||
|
||||
def run_qalc(expression):
|
||||
# Run qalc with the provided expression
|
||||
result = subprocess.run(["qalc", expression], stdout=subprocess.PIPE, text=True)
|
||||
output = result.stdout
|
||||
return output
|
||||
|
||||
|
||||
def format_expression_for_latex(expression):
|
||||
# Replace the units and special characters in the input expression using the dictionary
|
||||
for symbol, latex_symbol in latex_symbols.items():
|
||||
expression = expression.replace(symbol, latex_symbol)
|
||||
|
||||
return expression
|
||||
|
||||
|
||||
def format_result_for_latex(output):
|
||||
# Get the last line of qalc output (the answer)
|
||||
output = output.strip().split("\n")[-1]
|
||||
|
||||
# Check if the result is approximate (contains "≈")
|
||||
approx_match = "≈" in output
|
||||
|
||||
# Extract the value and unit from the result
|
||||
match = re.search(r"(≈|=)\s*([-\d\.]+)\s*(.*)", output)
|
||||
if match:
|
||||
_, value, unit = match.groups()
|
||||
# Replace the units and special characters using the dictionary
|
||||
unit = unit.strip()
|
||||
for symbol, latex_symbol in latex_symbols.items():
|
||||
unit = unit.replace(symbol, latex_symbol)
|
||||
|
||||
# Use = or \approx in LaTeX based on whether the result is approximate
|
||||
equal_symbol = r"\approx" if approx_match else "="
|
||||
|
||||
# Only add \, if there's a unit
|
||||
if unit:
|
||||
latex_result = f"{equal_symbol} {value.strip()} \\, {unit}"
|
||||
else:
|
||||
latex_result = f"{equal_symbol} {value.strip()}"
|
||||
|
||||
return latex_result
|
||||
return output # Fallback to returning the raw qalc output if no match
|
||||
|
||||
|
||||
def copy_to_clipboard(text):
|
||||
# Use wl-copy to send the text to the clipboard
|
||||
subprocess.run(["wl-copy"], input=text, text=True)
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
while True:
|
||||
# Input the qalc expression
|
||||
expression = input("\nEnter qalc expression (Ctrl+C to exit): ")
|
||||
|
||||
# Run qalc and get the result
|
||||
qalc_output = run_qalc(expression)
|
||||
|
||||
# Format the input expression for LaTeX
|
||||
latex_expression = format_expression_for_latex(expression)
|
||||
|
||||
# Format the output result for LaTeX
|
||||
latex_result = format_result_for_latex(qalc_output)
|
||||
|
||||
# Combine both input expression and result for final LaTeX output
|
||||
final_latex_output = f"{latex_expression} {latex_result}"
|
||||
|
||||
# Print and copy to clipboard
|
||||
print(f"Final LaTeX output:\n{final_latex_output}")
|
||||
copy_to_clipboard(final_latex_output)
|
||||
print("LaTeX result copied to clipboard!")
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\nExiting...")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
109
.ssh/config
109
.ssh/config
|
|
@ -8,12 +8,34 @@ Host crawler.kitsunehosting.net
|
|||
HostName crawler.kitsunehosting.net
|
||||
User joe
|
||||
|
||||
Host 10.12.33.234
|
||||
HostName 10.12.33.234
|
||||
User will
|
||||
host edge
|
||||
HostName 10.85.5.52
|
||||
User joe
|
||||
|
||||
Host 10.85.3.39
|
||||
HostName 10.85.3.39
|
||||
host portainer
|
||||
HostName 10.85.3.60
|
||||
User joe
|
||||
|
||||
host bitwarden
|
||||
HostName 10.85.3.83
|
||||
User joe
|
||||
|
||||
host frigate
|
||||
IdentityFile ~/.ssh/hydroxonium
|
||||
HostName 10.85.5.20
|
||||
User joe
|
||||
|
||||
host portainer
|
||||
IdentityFile ~/.ssh/hydroxonium
|
||||
HostName 10.85.3.60
|
||||
User joe
|
||||
|
||||
host crackheadbot
|
||||
HostName 10.85.3.182
|
||||
User joe
|
||||
|
||||
host dockerhost
|
||||
HostName 10.85.3.75
|
||||
User joe
|
||||
|
||||
Host cobalt
|
||||
|
|
@ -26,7 +48,7 @@ Host manganese
|
|||
User root
|
||||
|
||||
# R710
|
||||
Host magnesium
|
||||
Host nickel
|
||||
HostName 10.85.3.4
|
||||
User root
|
||||
|
||||
|
|
@ -37,24 +59,42 @@ Host romi
|
|||
|
||||
# My main NAS
|
||||
Host zinc
|
||||
IdentityFile ~/.ssh/hydroxonium
|
||||
HostName 10.85.3.120
|
||||
User joe
|
||||
|
||||
# APRS
|
||||
Host houston
|
||||
IdentityFile ~/.ssh/hydroxonium
|
||||
HostName 10.85.3.39
|
||||
User joe
|
||||
|
||||
# Steamcache
|
||||
Host steamcache
|
||||
HostName 10.85.3.60
|
||||
User joe
|
||||
Host deck
|
||||
HostName 10.85.3.221
|
||||
User deck
|
||||
|
||||
# booru Image Server
|
||||
Host booru
|
||||
IdentityFile ~/.ssh/hydroxonium
|
||||
HostName 10.85.3.71
|
||||
User joe
|
||||
|
||||
# Caddy
|
||||
Host caddy
|
||||
IdentityFile ~/.ssh/hydroxonium
|
||||
HostName 10.85.3.9
|
||||
User joe
|
||||
|
||||
# Robotics
|
||||
# 1721's main NAS
|
||||
Host flattoaster
|
||||
HostName 10.17.21.10
|
||||
PreferredAuthentications password
|
||||
IdentityFile none
|
||||
User root
|
||||
|
||||
Host robotics-vps
|
||||
HostName 50.116.52.250
|
||||
User root
|
||||
|
||||
Host lapis
|
||||
|
|
@ -74,14 +114,29 @@ Host aur.archlinux.org
|
|||
User aur
|
||||
|
||||
# Personal Git Server
|
||||
Host kitsunehosting.net
|
||||
Host ssh.kitsunehosting.net
|
||||
Port 2222
|
||||
IdentityFile ~/.ssh/gitea
|
||||
|
||||
# Github
|
||||
Host github.com
|
||||
Hostname ssh.github.com
|
||||
Port 443
|
||||
User git
|
||||
IdentityFile ~/.ssh/github
|
||||
|
||||
Host snowsunehost
|
||||
Hostname 10.85.3.65
|
||||
User joe
|
||||
|
||||
# Also github but, for snowsune
|
||||
Host github_snowsune
|
||||
HostName ssh.github.com
|
||||
User git
|
||||
Port 443
|
||||
IdentityFile ~/.ssh/github_snowsune
|
||||
IdentitiesOnly yes
|
||||
|
||||
# Bitbucket
|
||||
Host bitbucket.org
|
||||
IdentityFile ~/.ssh/bitbucket
|
||||
|
|
@ -91,13 +146,45 @@ Host devserver
|
|||
HostName 10.12.32.35
|
||||
User joe
|
||||
|
||||
Host bosch
|
||||
HostName 192.168.0.41
|
||||
#IdentityFile ~/.ssh/silvertech/ssh_host_dsa_key
|
||||
#UpdateHostKeys no
|
||||
|
||||
Host retropie
|
||||
HostName 10.19.80.2
|
||||
User pi
|
||||
IdentityFile ~/.ssh/silvertech/silvertech
|
||||
IdentitiesOnly yes
|
||||
|
||||
# The little docker host in the closet
|
||||
# Runs the vestabot image
|
||||
Host stdocker1
|
||||
HostName 10.20.30.225
|
||||
IdentityFile ~/.ssh/stdocker
|
||||
|
||||
# Little mini-pc in the IOT room
|
||||
Host pioremote
|
||||
# This is actually a zerotier ip
|
||||
HostName 10.144.0.15
|
||||
IdentityFile ~/.ssh/silvertech/silvertech
|
||||
IdentitiesOnly yes
|
||||
|
||||
# Work laptop
|
||||
Host workLaptop
|
||||
HostName 10.144.236.234
|
||||
IdentityFile ~/.ssh/hydroxonium
|
||||
|
||||
# Argen pi
|
||||
Host argen
|
||||
HostName 10.20.30.224
|
||||
|
||||
Host asterisk
|
||||
HostName 10.85.3.155
|
||||
IdentityFile ~/.ssh/hydroxonium
|
||||
|
||||
# Pterodactyal stuff
|
||||
Host wing1.kitsunehosting.net
|
||||
HostName wing1.kitsunehosting.net
|
||||
PreferredAuthentications password
|
||||
IdentityFile none
|
||||
|
|
|
|||
39
.zshrc
39
.zshrc
|
|
@ -1,12 +1,19 @@
|
|||
# Joe added this:
|
||||
uwufetch
|
||||
echo ""
|
||||
fortune
|
||||
fortune -a
|
||||
|
||||
# Add my bins to path
|
||||
path+=("$HOME/.local/bin")
|
||||
path+=("$HOME/Pictures/Furry/Scripts")
|
||||
export PATH
|
||||
|
||||
# LaTeX Import path
|
||||
export TEXINPUTS=/home/joe/College/SNHU/LaTeX:$(kpsepath tex | tr ':' '\n' | head -n 1):
|
||||
|
||||
# Not sure why but, needed to path these manually for ruby bin
|
||||
path+=("/home/joe/.local/share/gem/ruby/3.3.0/bin")
|
||||
|
||||
# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc.
|
||||
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
|
||||
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
|
||||
|
|
@ -123,6 +130,13 @@ fi
|
|||
# alias zshconfig="mate ~/.zshrc"
|
||||
# alias ohmyzsh="mate ~/.oh-my-zsh"
|
||||
|
||||
# https://www.reddit.com/r/archlinux/comments/p0y2hs/flatpak/
|
||||
alias update="paru -Syu --skipreview; flatpak update; sudo snap refresh"
|
||||
|
||||
# Power
|
||||
alias upshift="powerprofilesctl set performance"
|
||||
alias downshift="powerprofilesctl set power-saver"
|
||||
|
||||
[ -f ~/.fzf.zsh ] && source ~/.fzf.zsh
|
||||
|
||||
export FZF_DEFAULT_COMMAND='fdfind --type f'
|
||||
|
|
@ -130,3 +144,26 @@ export FZF_DEFAULT_OPTS="--layout=reverse --inline-info --height=80%"
|
|||
|
||||
# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh.
|
||||
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
|
||||
|
||||
|
||||
# This is fo cumtanks lol
|
||||
cumtanks() {
|
||||
cd /home/joe/git/Snowsune/cumtanks || return 1
|
||||
EPOCH=$(date +%s)
|
||||
DATA_JSON="www/data.json"
|
||||
tmpfile=$(mktemp)
|
||||
awk -v epoch="$EPOCH" '
|
||||
BEGIN { added=0 }
|
||||
/"logs"[ \t]*:[ \t]*\[/ { inlogs=1 }
|
||||
inlogs && /\]/ && !added {
|
||||
sub(/\]/, " ,{ \"date\": " epoch ", \"text\": \"\" }\n ]")
|
||||
added=1
|
||||
}
|
||||
{ print }
|
||||
' "$DATA_JSON" > "$tmpfile" && mv "$tmpfile" "$DATA_JSON"
|
||||
sed -i 's/,\n ]/\n ]/' "$DATA_JSON"
|
||||
code -g "$DATA_JSON":$(grep -n '"logs"' "$DATA_JSON" | cut -d: -f1)
|
||||
}
|
||||
|
||||
# For chat GPT
|
||||
export OPENAI_API_KEY="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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue