some new things, mostly not working or finished

This commit is contained in:
2025-11-08 18:16:10 +01:00
parent 5792bfbd9a
commit 795fb42900
30 changed files with 4789 additions and 1 deletions

View File

@@ -3,7 +3,6 @@ import tkinter as tk
class Fibonacci:
def s5(self, n, r):
"""Generate Fibonacci spiral polar coordinates"""
spirals = []
phi = (1 + 5 ** 0.5) / 2 # golden ratio
for i in range(n + 1):

46
fib/fib.c Normal file
View File

@@ -0,0 +1,46 @@
#include <stdio.h>
#include <stdint.h>
// tiny struct to hold a 2x2 matrix
typedef struct {
uint64_t m00, m01, m10, m11;
} Matrix2x2;
// multiply two 2x2 matrices together
Matrix2x2 multiply(Matrix2x2 a, Matrix2x2 b) {
// standard 2x2 matrix multiplication
return (Matrix2x2) {
a.m00* b.m00 + a.m01 * b.m10, // top-left
a.m00* b.m01 + a.m01 * b.m11, // top-right
a.m10* b.m00 + a.m11 * b.m10, // bottom-left
a.m10* b.m01 + a.m11 * b.m11 // bottom-right
};
}
// raise a matrix to the power n using binary exponentiation
Matrix2x2 matrix_pow(Matrix2x2 base, int n) {
Matrix2x2 result = { 1, 0, 0, 1 }; // start with identity
while (n > 0) {
if (n % 2 == 1) result = multiply(result, base); // if odd, multiply once
base = multiply(base, base); // square the base
n /= 2; // integer divide n by 2
}
return result;
}
// get nth fibonacci number using matrix exponentiation
uint64_t fibonacci_matrix(int n) {
if (n == 0) return 0; // edge case
Matrix2x2 base = { 1, 1, 1, 0 }; // Fibonacci Q-matrix
Matrix2x2 result = matrix_pow(base, n - 1); // raise to (n-1)
return result.m00; // top-left is F(n)
}
int main() {
int n = 50; // how many numbers to print
for (int i = 0; i < n; i++) {
printf("%llu ", fibonacci_matrix(i)); // print each fib number
}
printf("\n"); // newline at end
return 0;
}

22
fib/fib.java Normal file
View File

@@ -0,0 +1,22 @@
public class recursive {
public static int fibonacci(int max) {
if (max <= 1) {
return max;
}
else {
return fibonacci(max - 2) + fibonacci(max - 1);
}
}
public static void main(String[] args) {
int max = 10;
for (int i = 0; i < max; i++) {
System.out.print(fibonacci(i) + " ");
}
}
}

41
fib/fib.py Normal file
View File

@@ -0,0 +1,41 @@
# tiny class to hold a 2x2 matrix
class Matrix2x2:
def __init__(self, m00, m01, m10, m11):
self.m00 = m00
self.m01 = m01
self.m10 = m10
self.m11 = m11
# multiply two 2x2 matrices
def multiply(a, b):
return Matrix2x2(
a.m00 * b.m00 + a.m01 * b.m10, # top-left
a.m00 * b.m01 + a.m01 * b.m11, # top-right
a.m10 * b.m00 + a.m11 * b.m10, # bottom-left
a.m10 * b.m01 + a.m11 * b.m11 # bottom-right
)
# raise a matrix to the power n using binary exponentiation
def matrix_pow(base, n):
result = Matrix2x2(1, 0, 0, 1) # start with identity matrix
while n > 0:
if n % 2 == 1: # if n is odd, multiply once
result = multiply(result, base)
base = multiply(base, base) # square the base
n //= 2 # integer division
return result
# get nth fibonacci number using matrix exponentiation
def fibonacci_matrix(n):
if n == 0:
return 0 # edge case
base = Matrix2x2(1, 1, 1, 0) # Fibonacci Q-matrix
result = matrix_pow(base, n - 1)
return result.m00 # top-left is F(n)
# print first n fibonacci numbers
if __name__ == "__main__":
n = 50
for i in range(n):
print(fibonacci_matrix(i), end=" ")
print()

22
fib/sfib.c Normal file
View File

@@ -0,0 +1,22 @@
#include <stdio.h>
typedef unsigned long long U;
U fib(int n) {
if (n < 2) return n;
U a = 1, b = 1, c = 1, d = 0, x = 1, y = 0, z = 0, w = 1, t;
while (n) {
if (n & 1) {
t = x * a + y * c; y = x * b + y * d; x = t;
t = z * a + w * c; w = z * b + w * d; z = t;
}
t = a * a + b * c; b = a * b + b * d; a = t;
t = c * a + d * c; d = c * b + d * d; c = t;
n >>= 1;
}
return x;
}
int main(int argc, char **argv[]) {
for (int i = 0;i < int(argv);i++) printf("%llu ", fib(i));
}

5
functions/firstlast.py Normal file
View File

@@ -0,0 +1,5 @@
def wort_funktion(s : str) -> str:
last = len(s)
print(s[0] + s[(len(s)) - 1])
wort_funktion("das ist ein string!")

70
functions/mark/main.py Normal file
View File

@@ -0,0 +1,70 @@
# Datei <20>ffnen
f = open("login.txt", "a")
#Login Prozess
def login():
benutzername = input("Geben sie ihre Benutzername ein:")
passwort = input("Geben Sie Ihren Passwort ein:")
global Registrierung
Registrierung = False #Sagt dem Programm ob der Nutzer in Zukunft registrieren m<>chte
falscherPasswort = False
try:
# Auslesen der Datei
with open("login.txt", "r") as f:
accounts = f.readlines()
angemeldet = False
for account in accounts:
name, pw = account.strip().split(",")
if benutzername == name and passwort == pw:
angemeldet = True
falscherPasswort = False
break
elif benutzername == name and not passwort == pw:
falscherPasswort = True
if angemeldet and not falscherPasswort:
print("Anmeldung erfolgreich!")
elif falscherPasswort:
print("Benutzername oder Passwort falsch (pw)")
else:
print("Benutzername oder Passwort falsch")
newaccount = input("Wollen Sie einen neuen Account erstellen?")
if newaccount == "Ja":
Registrierung = True
else:
Registrierung = False
except FileNotFoundError:
print("Datei wurde nicht gefunden")
# Funktion zur Registrierung
def signin():
newname = input("Geben Sie ihr neuer Benutzername ein:")
newpw = input("Geben Sie ihr neues Passwort ein:")
with open("login.txt", "a") as f:
f.write("\n"+newname+",")
f.write(newpw+"\n")
print("Dein Account wurde erfolgreich registriert!")
login()
# Aufruf der Funktion falls der Nutzer registrieren will
if Registrierung:
signin()

52
functions/passwort.py Normal file
View File

@@ -0,0 +1,52 @@
import random
def passwort_zahlen(laenge):
p = ""
for _ in range(laenge):
zufallszahl = random.randint(0, 9)
p = str(p) + str(zufallszahl)
return p
def passwort_buchstaben(laenge):
p = ""
for _ in range(laenge):
buchstabe = random.choice("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
p = str(p) + str(buchstabe)
return p
def passwort_kombi(laenge):
p = ""
for _ in range(laenge):
wahl = random.choice(["zahl", "buchstabe"])
if wahl == "zahl":
zufallszahl = random.randint(0, 9)
p = str(p) + str(zufallszahl)
else:
buchstabe = random.choice("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
p = str(p) + str(buchstabe)
return p
# Hauptprogramm
def main():
print("Passwortgenerator")
print("1: Nur Zahlen")
print("2: Nur Buchstaben")
print("3: Zahlen und Buchstaben")
auswahl = input("Deine Wahl (1/2/3): ")
laenge = int(input("Wie viele Zeichen soll das Passwort haben? "))
if auswahl == "1":
passwort = passwort_zahlen(laenge)
elif auswahl == "2":
passwort = passwort_buchstaben(laenge)
elif auswahl == "3":
passwort = passwort_kombi(laenge)
else:
return
print("Passwort:", passwort)
# Programm starten
if __name__ == "__main__":
main()

57
functions/sekunden.py Normal file
View File

@@ -0,0 +1,57 @@
# -*- coding: ansi -*-
def timetosec(time_str):
parts = time_str.split(':')
parts = [int(p) for p in parts]
if len(parts) == 3: # HH:MM:SS
hours, minutes, seconds = parts
elif len(parts) == 2: # MM:SS
hours = 0
minutes, seconds = parts
elif len(parts) == 1: # SS
hours = 0
minutes = 0
seconds = parts[0]
else:
raise ValueError("Zeitformat")
total_seconds = hours * 3600 + minutes * 60 + seconds
return total_seconds
print(timetosec("02:15:30")) # 8130
print(timetosec("15:30")) # 930
print(timetosec("45")) # 45
def format_seconds(seconds: int) -> str:
"""
Meine Interpretation von dem Moodle eintrag. Der leer ist.
Wandelt Sekunden in das n<>chst beste Format um
:param seconds -> int
:returns str
"""
if seconds < 0:
return "falsche eingable"
intervals = (
('Tag', 86400), # 60*60*24
('Stunde', 3600), # 60*60
('Minute', 60),
('Sekunde', 1),
)
result = []
for name, count in intervals:
value = seconds // count
if value:
seconds -= value * count
if value == 1:
result.append(f"{value} {name}")
else:
result.append(f"{value} {name}en")
return ', '.join(result) if result else "0 Sekunden"
print(format_seconds(3661)) # 1std 1m 1s
print(format_seconds(86465)) # 1t 1m 5s
print(format_seconds(59)) # 59s

18
functions/summe.py Normal file
View File

@@ -0,0 +1,18 @@
def summe(n) -> float:
lst = []
for i in range(1,n):
lst.append(i)
formatted = " + ".join(str(x) for x in lst)
print(f"summe = {formatted} ; sum = {total(lst)}")
def total(lst: list) -> float:
total = None
for i in lst:
i += total
if total:
return total
else:
return None
summe(10)

31
functions/umrechner.py Normal file
View File

@@ -0,0 +1,31 @@
def euroToDollar(euro_amount, kurs=1.15):
return euro_amount * kurs
def dollarToEuro(dollar_amount, kurs=1.15):
return dollar_amount / kurs
def main():
print("WährungsUmrechner ")
print("Aktueller Kurs: 1€ = {:.2f}USD".format(1.15))
richtung = input("1) € auf $ \n 2) $ auf € \nIhre Wahl: ")
if richtung == "1":
euro_str = input("€: ")
try:
euro = float(euro_str.replace(",","."))
dollar = euroToDollar(euro, kurs=1.15)
print(f"{euro:.2f}€ entsprechen {dollar:.2f}$")
except ValueError as e:
print(e)
elif richtung == "2":
usd_str = input("$: ")
try:
usd = float(usd_str.replace(",","."))
euro = dollarToEuro(usd, kurs=1.15)
print(f"{usd:.2f}$ entsprechen {euro:.2f}")
except ValueError as e:
print(e)
else:
print("wähle richtig")
if __name__ == "__main__":
main()

143
key/key.py Normal file
View File

@@ -0,0 +1,143 @@
import cv2
import numpy as np
import trimesh
from shapely.geometry import Polygon
import tkinter as tk
from tkinter import filedialog, messagebox
from PIL import Image, ImageTk
import os
class KeyForge3DApp:
def __init__(self, root):
self.root = root
self.root.title("KeyForge3D - Key Shape Extractor and 3D Model Generator")
self.root.geometry("600x400")
# Variables
self.image_path = None
self.scale_factor = 0.1 # Default scale: 1 pixel = 0.1 mm (adjust with a reference object if needed)
self.key_thickness = 2.0 # Thickness of the key in mm
self.num_cuts = 5 # Number of bitting cuts (adjust based on key type)
self.cut_depth_increment = 0.33 # Depth increment per bitting value in mm (e.g., Schlage standard)
# GUI Elements
self.label = tk.Label(root, text="KeyForge3D: Extract and 3D Print Keys", font=("Arial", 16))
self.label.pack(pady=10)
self.upload_button = tk.Button(root, text="Upload Key Image", command=self.upload_image)
self.upload_button.pack(pady=5)
self.image_label = tk.Label(root)
self.image_label.pack(pady=5)
self.process_button = tk.Button(root, text="Process Key and Generate 3D Model", command=self.process_key, state=tk.DISABLED)
self.process_button.pack(pady=5)
self.result_label = tk.Label(root, text="", font=("Arial", 12))
self.result_label.pack(pady=5)
def upload_image(self):
"""Allow the user to upload an image of a key."""
self.image_path = filedialog.askopenfilename(filetypes=[("Image Files", "*.jpg *.jpeg *.png")])
if self.image_path:
# Display the uploaded image
img = Image.open(self.image_path)
img = img.resize((300, 150), Image.Resampling.LANCZOS) # Resize for display
img_tk = ImageTk.PhotoImage(img)
self.image_label.config(image=img_tk)
self.image_label.image = img_tk # Keep a reference to avoid garbage collection
self.process_button.config(state=tk.NORMAL)
self.result_label.config(text="Image uploaded. Click 'Process Key' to generate the 3D model.")
def process_key(self):
"""Process the key image, extract the shape, and generate a 3D model."""
if not self.image_path:
messagebox.showerror("Error", "Please upload an image first.")
return
try:
# Load the image
image = cv2.imread(self.image_path)
if image is None:
raise ValueError("Could not load the image.")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Apply Gaussian blur and edge detection
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edges = cv2.Canny(blurred, 50, 150)
# Find contours
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Filter contours to find the key (long, thin shape)
key_contour = None
for contour in contours:
perimeter = cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, 0.02 * perimeter, True)
x, y, w, h = cv2.boundingRect(contour)
aspect_ratio = w / float(h)
if 2 < aspect_ratio < 5 and w > 100: # Adjust these values based on your image
key_contour = contour
break
if key_contour is None:
raise ValueError("Could not detect a key in the image.")
# Extract the key region
x, y, w, h = cv2.boundingRect(key_contour)
key_region = gray[y:y+h, x:x+w]
# Convert contour to a 2D polygon
points = key_contour.reshape(-1, 2) * self.scale_factor
key_polygon = Polygon(points)
# Extrude the polygon to create a 3D model
key_mesh = trimesh.creation.extrude_polygon(key_polygon, height=self.key_thickness)
# Analyze the bitting
blade = key_region[h//2:h, :] # Focus on the lower half (blade)
height, width = blade.shape
segment_width = width // self.num_cuts
bitting = []
for i in range(self.num_cuts):
segment = blade[:, i * segment_width:(i + 1) * segment_width]
# Find the highest point (shallowest cut) in the segment
cut_depth = np.argmax(segment, axis=0).mean()
# Scale the depth to real-world dimensions
depth_value = (cut_depth / height) * self.cut_depth_increment * 9
bitting.append(depth_value)
# Apply bitting cuts to the 3D model
for i, depth in enumerate(bitting):
cut_x = (i * segment_width * self.scale_factor) + (segment_width * self.scale_factor / 2)
cut_y = 0 # Adjust based on blade position
cut_width = segment_width * self.scale_factor
cut_height = depth
# Create a box for the cut and subtract it from the key mesh
cut_box = trimesh.creation.box(
extents=[cut_width, cut_height, self.key_thickness + 1],
transform=trimesh.transformations.translation_matrix([cut_x, cut_y, 0])
)
key_mesh = key_mesh.difference(cut_box)
# Export the 3D model as STL
output_path = "key_model.stl"
key_mesh.export(output_path)
# Display the results
bitting_code = [int(d / self.cut_depth_increment) for d in bitting]
self.result_label.config(
text=f"Success! Bitting Code: {bitting_code}\n3D Model saved as '{output_path}'"
)
messagebox.showinfo("Success", f"3D model generated and saved as '{output_path}'.")
except Exception as e:
messagebox.showerror("Error", f"Failed to process the key: {str(e)}")
self.result_label.config(text="Error processing the key. See error message.")
if __name__ == "__main__":
root = tk.Tk()
app = KeyForge3DApp(root)
root.mainloop()

847
key/main.py Normal file
View File

@@ -0,0 +1,847 @@
"""
Key to STL Generator - Educational Tool
Analyzes key images and creates 3D STL models of the key teeth profile
Enhanced version with manual selection tools
"""
from PIL import Image, ImageDraw, ImageFilter, ImageTk, ImageEnhance
import numpy as np
from scipy import ndimage, signal
from scipy.ndimage import median_filter, gaussian_filter
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import struct
class KeyToSTL:
def __init__(self):
self.original_image = None
self.image = None
self.processed_image = None
self.key_profile = []
self.key_bounds = None
self.manual_bounds = None
self.groove_regions = []
def load_image(self, filepath):
"""Load and preprocess the key image"""
self.original_image = Image.open(filepath)
self.original_image = self.original_image.convert('RGB')
self.image = self.original_image.convert('L')
print(f"Image loaded: {self.image.size}")
return self.image
def set_manual_bounds(self, x1, y1, x2, y2):
"""Set manual key bounds from user selection"""
self.manual_bounds = {
'left': min(x1, x2),
'right': max(x1, x2),
'top': min(y1, y2),
'bottom': max(y1, y2)
}
print(f"Manual bounds set: {self.manual_bounds}")
def add_groove_region(self, x1, y1, x2, y2):
"""Add a groove region that should be emphasized"""
region = {
'left': min(x1, x2),
'right': max(x1, x2),
'top': min(y1, y2),
'bottom': max(y1, y2)
}
self.groove_regions.append(region)
print(f"Groove region added: {region}")
def clear_groove_regions(self):
"""Clear all groove regions"""
self.groove_regions = []
print("Groove regions cleared")
def denoise_image(self, median_size=3, gaussian_sigma=1.5):
"""Apply strong denoising to handle static/noise in images"""
if self.image is None:
raise ValueError("No image loaded")
img_array = np.array(self.image)
denoised = median_filter(img_array, size=median_size)
denoised = gaussian_filter(denoised, sigma=gaussian_sigma)
return denoised
def auto_detect_key(self, noise_handling=True):
"""Automatically detect the key region with noise handling"""
if self.image is None:
raise ValueError("No image loaded")
if noise_handling:
img_array = self.denoise_image(median_size=5, gaussian_sigma=2.0)
else:
img_array = np.array(self.image)
img_pil = Image.fromarray(img_array.astype(np.uint8))
enhancer = ImageEnhance.Contrast(img_pil)
enhanced = enhancer.enhance(2.5)
img_array = np.array(enhanced)
img_array = gaussian_filter(img_array, sigma=1.0)
sobel_x = ndimage.sobel(img_array, axis=1)
sobel_y = ndimage.sobel(img_array, axis=0)
edge_magnitude = np.hypot(sobel_x, sobel_y)
edge_magnitude = (edge_magnitude / edge_magnitude.max() * 255).astype(np.uint8)
threshold = np.percentile(edge_magnitude[edge_magnitude > 0], 90)
binary_edges = edge_magnitude > threshold
from scipy.ndimage import binary_closing, binary_opening, binary_dilation, binary_erosion
binary_edges = binary_opening(binary_edges, structure=np.ones((2, 2)))
binary_edges = binary_closing(binary_edges, structure=np.ones((3, 3)))
binary_edges = binary_erosion(binary_edges, structure=np.ones((2, 2)))
binary_edges = binary_dilation(binary_edges, structure=np.ones((2, 2)))
self.processed_image = Image.fromarray((binary_edges * 255).astype(np.uint8))
return self.processed_image
def find_key_bounds(self, margin_percent=0.1):
"""Find the bounding box of the key with noise-resistant algorithm"""
if self.processed_image is None:
raise ValueError("Process image first")
# Use manual bounds if available
if self.manual_bounds:
self.key_bounds = self.manual_bounds.copy()
print(f"Using manual bounds: {self.key_bounds}")
return self.key_bounds
edges_array = np.array(self.processed_image)
height, width = edges_array.shape
row_content = np.sum(edges_array, axis=1)
col_content = np.sum(edges_array, axis=0)
row_smooth = gaussian_filter(row_content.astype(float), sigma=height * 0.02)
col_smooth = gaussian_filter(col_content.astype(float), sigma=width * 0.01)
row_threshold = np.percentile(row_smooth[row_smooth > 0], 25)
col_threshold = np.percentile(col_smooth[col_smooth > 0], 15)
rows_with_content = np.where(row_smooth > row_threshold)[0]
cols_with_content = np.where(col_smooth > col_threshold)[0]
if len(rows_with_content) > 0 and len(cols_with_content) > 0:
row_margin = int(height * margin_percent)
col_margin = int(width * margin_percent * 0.5)
self.key_bounds = {
'top': max(0, rows_with_content[0] - row_margin),
'bottom': min(height - 1, rows_with_content[-1] + row_margin),
'left': max(0, cols_with_content[0] - col_margin),
'right': min(width - 1, cols_with_content[-1] + col_margin)
}
print(f"Key bounds detected: {self.key_bounds}")
else:
self.key_bounds = {
'top': height // 4,
'bottom': 3 * height // 4,
'left': width // 10,
'right': 9 * width // 10
}
print("Using fallback bounds")
return self.key_bounds
def extract_key_profile(self, use_top=True, consensus_window=5):
"""Extract key profile with noise-resistant consensus approach"""
if self.processed_image is None or self.key_bounds is None:
raise ValueError("Process image and find bounds first")
edges_array = np.array(self.processed_image)
bounds = self.key_bounds
roi = edges_array[bounds['top']:bounds['bottom'],
bounds['left']:bounds['right']]
height, width = roi.shape
profile = []
for x in range(width):
start_col = max(0, x - consensus_window // 2)
end_col = min(width, x + consensus_window // 2 + 1)
window = roi[:, start_col:end_col]
edge_positions = []
for col in range(window.shape[1]):
column = window[:, col]
edge_pixels = np.where(column > 128)[0]
if len(edge_pixels) > 0:
if use_top:
edge_positions.append(edge_pixels[0])
else:
edge_positions.append(edge_pixels[-1])
if len(edge_positions) > 0:
y_pos = int(np.median(edge_positions))
actual_y = y_pos + bounds['top']
actual_x = x + bounds['left']
profile.append((actual_x, actual_y))
else:
if len(profile) > 0:
profile.append((x + bounds['left'], profile[-1][1]))
else:
profile.append((x + bounds['left'], bounds['top'] + height // 2))
profile = self._fill_profile_gaps(profile)
# Apply groove emphasis
if self.groove_regions:
profile = self._emphasize_grooves(profile)
self.key_profile = profile
return profile
def _emphasize_grooves(self, profile, emphasis_factor=1.5):
"""Emphasize groove regions in the profile"""
emphasized = []
for x, y in profile:
# Check if point is in any groove region
in_groove = False
for groove in self.groove_regions:
if groove['left'] <= x <= groove['right']:
in_groove = True
break
if in_groove:
# Find average Y in this region
region_ys = [py for px, py in profile if groove['left'] <= px <= groove['right']]
if region_ys:
avg_y = np.mean(region_ys)
# Emphasize deviation from average
deviation = y - avg_y
new_y = avg_y + deviation * emphasis_factor
emphasized.append((x, int(new_y)))
else:
emphasized.append((x, y))
else:
emphasized.append((x, y))
return emphasized
def _fill_profile_gaps(self, profile, max_gap=10):
"""Fill gaps in profile with interpolation"""
if len(profile) < 2:
return profile
filled = [profile[0]]
for i in range(1, len(profile)):
prev_x, prev_y = filled[-1]
curr_x, curr_y = profile[i]
if abs(curr_y - prev_y) > max_gap:
steps = int(abs(curr_x - prev_x))
if steps > 1:
for j in range(1, steps):
interp_x = prev_x + j
interp_y = int(prev_y + (curr_y - prev_y) * j / steps)
filled.append((interp_x, interp_y))
filled.append((curr_x, curr_y))
return filled
def smooth_profile_advanced(self, window_size=11, poly_order=3):
"""Apply Savitzky-Golay filter with noise handling"""
if not self.key_profile:
raise ValueError("Extract profile first")
profile_array = np.array(self.key_profile)
x_coords = profile_array[:, 0]
y_coords = profile_array[:, 1]
y_median = median_filter(y_coords, size=5)
window_size = min(window_size, len(y_median))
if window_size % 2 == 0:
window_size -= 1
window_size = max(window_size, poly_order + 2)
if window_size % 2 == 0:
window_size += 1
try:
smoothed_y = signal.savgol_filter(y_median, window_size, poly_order)
except:
smoothed_y = gaussian_filter(y_median, sigma=window_size / 3.0)
self.key_profile = [(int(x), int(y)) for x, y in zip(x_coords, smoothed_y)]
return self.key_profile
def remove_outliers(self, threshold=3.0, window_size=10):
"""Remove outlier points using local statistics"""
if not self.key_profile:
return
profile_array = np.array(self.key_profile)
y_coords = profile_array[:, 1].astype(float)
cleaned_y = y_coords.copy()
for i in range(len(y_coords)):
start = max(0, i - window_size // 2)
end = min(len(y_coords), i + window_size // 2 + 1)
window = y_coords[start:end]
local_median = np.median(window)
mad = np.median(np.abs(window - local_median))
if mad > 0:
z_score = abs(y_coords[i] - local_median) / (1.4826 * mad)
if z_score > threshold:
cleaned_y[i] = local_median
self.key_profile = [(int(x), int(y)) for x, y in zip(profile_array[:, 0], cleaned_y)]
return self.key_profile
def normalize_profile(self):
"""Normalize profile to 0-1 range for depth"""
if not self.key_profile:
raise ValueError("Extract profile first")
profile_array = np.array(self.key_profile)
y_coords = profile_array[:, 1]
y_min, y_max = y_coords.min(), y_coords.max()
if y_max - y_min < 1:
return [(x, 0.5) for x, _ in self.key_profile]
normalized = []
for x, y in self.key_profile:
normalized_depth = (y - y_min) / (y_max - y_min)
normalized.append((x, normalized_depth))
return normalized
def generate_stl(self, output_path, depth_scale=2.0, base_thickness=1.0):
"""Generate STL file from the key profile"""
if not self.key_profile:
raise ValueError("Extract profile first")
normalized_profile = self.normalize_profile()
profile_array = np.array(self.key_profile)
x_coords = profile_array[:, 0]
x_min, x_max = x_coords.min(), x_coords.max()
key_length = float(x_max - x_min)
key_width = 10.0
scaled_profile = []
for (x, _), (_, normalized_depth) in zip(self.key_profile, normalized_profile):
scaled_x = float(x - x_min)
actual_depth = normalized_depth * depth_scale
scaled_profile.append((scaled_x, actual_depth))
vertices, faces = self._create_mesh(scaled_profile, key_width, base_thickness)
self._write_stl(output_path, vertices, faces)
print(f"STL file saved: {output_path}")
print(f"Dimensions: {key_length:.1f} x {key_width:.1f} x {base_thickness + depth_scale:.1f} units")
def _create_mesh(self, profile, width, base_thickness):
"""Create 3D mesh from 2D profile"""
vertices = []
faces = []
n = len(profile)
for x, depth in profile:
z_top = base_thickness + depth
vertices.append([x, 0.0, z_top])
vertices.append([x, width, z_top])
vertices.append([x, 0.0, 0.0])
vertices.append([x, width, 0.0])
for i in range(n - 1):
base = i * 4
next_base = (i + 1) * 4
v1, v2 = base, base + 1
v3, v4 = next_base, next_base + 1
faces.append([v1, v3, v2])
faces.append([v2, v3, v4])
v1, v2 = base + 2, base + 3
v3, v4 = next_base + 2, next_base + 3
faces.append([v1, v2, v3])
faces.append([v2, v4, v3])
v1, v2 = base, base + 2
v3, v4 = next_base, next_base + 2
faces.append([v1, v3, v2])
faces.append([v2, v3, v4])
v1, v2 = base + 1, base + 3
v3, v4 = next_base + 1, next_base + 3
faces.append([v1, v2, v3])
faces.append([v2, v4, v3])
faces.append([0, 2, 1])
faces.append([1, 2, 3])
last = (n - 1) * 4
faces.append([last, last + 1, last + 2])
faces.append([last + 1, last + 3, last + 2])
return vertices, faces
def _write_stl(self, filepath, vertices, faces):
"""Write vertices and faces to binary STL file"""
with open(filepath, 'wb') as f:
header = b'Key STL - Python Generated' + b' ' * (80 - 26)
f.write(header)
f.write(struct.pack('<I', len(faces)))
for face in faces:
v1 = vertices[face[0]]
v2 = vertices[face[1]]
v3 = vertices[face[2]]
normal = self._calculate_normal(v1, v2, v3)
f.write(struct.pack('<fff', *normal))
f.write(struct.pack('<fff', *v1))
f.write(struct.pack('<fff', *v2))
f.write(struct.pack('<fff', *v3))
f.write(struct.pack('<H', 0))
def _calculate_normal(self, v1, v2, v3):
"""Calculate face normal vector"""
v1, v2, v3 = np.array(v1), np.array(v2), np.array(v3)
edge1 = v2 - v1
edge2 = v3 - v1
normal = np.cross(edge1, edge2)
length = np.linalg.norm(normal)
if length > 0:
normal = normal / length
else:
normal = np.array([0.0, 0.0, 1.0])
return normal.tolist()
class KeySTLGUI:
def __init__(self, root):
self.root = root
self.root.title("Key to STL Generator - Manual Selection Tools")
self.root.geometry("1200x800")
self.key_processor = KeyToSTL()
self.canvas_image = None
self.photo_image = None
# Selection state
self.selection_mode = None # 'key_bounds' or 'groove'
self.selection_start = None
self.current_rect = None
self.drawn_rectangles = []
self._create_widgets()
self._bind_canvas_events()
def _create_widgets(self):
main_container = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL)
main_container.pack(fill=tk.BOTH, expand=True)
# Control panel
control_frame = ttk.Frame(main_container, padding="10")
main_container.add(control_frame, weight=0)
ttk.Label(control_frame, text="Key to STL Generator",
font=('Arial', 14, 'bold')).pack(pady=10)
# Step 1
ttk.Label(control_frame, text="Step 1: Load Image",
font=('Arial', 10, 'bold')).pack(pady=(10, 5))
ttk.Button(control_frame, text="📁 Load Image",
command=self.load_image, width=22).pack(pady=5)
ttk.Separator(control_frame, orient='horizontal').pack(fill='x', pady=10)
# Step 2 - Manual Selection Tools
ttk.Label(control_frame, text="Step 2: Manual Selection (Optional)",
font=('Arial', 10, 'bold')).pack(pady=(5, 5))
tool_frame = ttk.LabelFrame(control_frame, text="Selection Tools", padding="5")
tool_frame.pack(fill='x', pady=5)
ttk.Button(tool_frame, text="🔲 Select Key Area",
command=self.start_key_selection, width=20).pack(pady=2)
ttk.Label(tool_frame, text="Drag rectangle around key",
font=('Arial', 8), foreground='gray').pack()
ttk.Separator(tool_frame, orient='horizontal').pack(fill='x', pady=5)
ttk.Button(tool_frame, text="🎯 Mark Groove Region",
command=self.start_groove_selection, width=20).pack(pady=2)
ttk.Label(tool_frame, text="Mark areas with teeth cuts",
font=('Arial', 8), foreground='gray').pack()
ttk.Button(tool_frame, text="🗑️ Clear Grooves",
command=self.clear_grooves, width=20).pack(pady=2)
ttk.Button(tool_frame, text="↩️ Cancel Selection",
command=self.cancel_selection, width=20).pack(pady=2)
self.selection_status = ttk.Label(tool_frame, text="No active tool",
font=('Arial', 8, 'italic'), foreground='blue')
self.selection_status.pack(pady=5)
ttk.Separator(control_frame, orient='horizontal').pack(fill='x', pady=10)
# Step 3 - Auto Process
ttk.Label(control_frame, text="Step 3: Auto Process",
font=('Arial', 10, 'bold')).pack(pady=(5, 5))
self.noise_handling_var = tk.BooleanVar(value=True)
ttk.Checkbutton(control_frame, text="Enable noise reduction",
variable=self.noise_handling_var).pack(pady=2)
ttk.Button(control_frame, text="🔍 Auto Detect Key",
command=self.auto_process, width=22).pack(pady=5)
self.edge_top_var = tk.BooleanVar(value=True)
ttk.Checkbutton(control_frame, text="Use top edge",
variable=self.edge_top_var).pack(pady=2)
ttk.Separator(control_frame, orient='horizontal').pack(fill='x', pady=10)
# Step 4 - Refine
ttk.Label(control_frame, text="Step 4: Refine Profile",
font=('Arial', 10, 'bold')).pack(pady=(5, 5))
ttk.Label(control_frame, text="Smoothing:").pack()
self.smooth_var = tk.IntVar(value=11)
ttk.Scale(control_frame, from_=5, to=21, variable=self.smooth_var,
orient=tk.HORIZONTAL).pack()
ttk.Button(control_frame, text="✨ Smooth",
command=self.smooth_profile, width=22).pack(pady=2)
ttk.Button(control_frame, text="🧹 Remove Outliers",
command=self.remove_outliers, width=22).pack(pady=2)
ttk.Separator(control_frame, orient='horizontal').pack(fill='x', pady=10)
# Step 5 - Export
ttk.Label(control_frame, text="Step 5: Export STL",
font=('Arial', 10, 'bold')).pack(pady=(5, 5))
ttk.Label(control_frame, text="Teeth Depth (mm):").pack()
self.depth_var = tk.DoubleVar(value=2.0)
ttk.Entry(control_frame, textvariable=self.depth_var, width=15).pack()
ttk.Label(control_frame, text="Base (mm):").pack()
self.base_var = tk.DoubleVar(value=1.0)
ttk.Entry(control_frame, textvariable=self.base_var, width=15).pack()
ttk.Button(control_frame, text="💾 Generate STL",
command=self.generate_stl, width=22).pack(pady=10)
# Canvas
canvas_frame = ttk.Frame(main_container)
main_container.add(canvas_frame, weight=1)
self.canvas = tk.Canvas(canvas_frame, bg='#f0f0f0', highlightthickness=0, cursor='crosshair')
h_scrollbar = ttk.Scrollbar(canvas_frame, orient=tk.HORIZONTAL, command=self.canvas.xview)
v_scrollbar = ttk.Scrollbar(canvas_frame, orient=tk.VERTICAL, command=self.canvas.yview)
self.canvas.configure(xscrollcommand=h_scrollbar.set, yscrollcommand=v_scrollbar.set)
h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# Status
self.status_label = ttk.Label(self.root, text="Ready - Load a key image to start",
relief=tk.SUNKEN, padding=(5, 2))
self.status_label.pack(side=tk.BOTTOM, fill=tk.X)
def _bind_canvas_events(self):
"""Bind mouse events for manual selection"""
self.canvas.bind('<Button-1>', self.on_canvas_click)
self.canvas.bind('<B1-Motion>', self.on_canvas_drag)
self.canvas.bind('<ButtonRelease-1>', self.on_canvas_release)
def start_key_selection(self):
"""Start key bounds selection mode"""
if self.photo_image is None:
messagebox.showwarning("Warning", "Please load an image first")
return
self.selection_mode = 'key_bounds'
self.selection_status.config(text="✓ Drag rectangle around key area")
self.canvas.config(cursor='crosshair')
self.status_label.config(text="SELECTION MODE: Draw rectangle around the key")
def start_groove_selection(self):
"""Start groove region selection mode"""
if self.photo_image is None:
messagebox.showwarning("Warning", "Please load an image first")
return
self.selection_mode = 'groove'
self.selection_status.config(text="✓ Drag rectangle over groove area")
self.canvas.config(cursor='crosshair')
self.status_label.config(text="GROOVE MODE: Draw rectangles over areas with teeth cuts")
def cancel_selection(self):
"""Cancel current selection mode"""
self.selection_mode = None
self.selection_start = None
if self.current_rect:
self.canvas.delete(self.current_rect)
self.current_rect = None
self.selection_status.config(text="No active tool")
self.canvas.config(cursor='')
self.status_label.config(text="Selection cancelled")
def clear_grooves(self):
"""Clear all groove regions"""
self.key_processor.clear_groove_regions()
# Redraw without groove rectangles
self._redraw_with_annotations()
self.status_label.config(text="✓ Groove regions cleared")
def on_canvas_click(self, event):
"""Handle canvas click for selection start"""
if self.selection_mode:
self.selection_start = (self.canvas.canvasx(event.x),
self.canvas.canvasy(event.y))
if self.current_rect:
self.canvas.delete(self.current_rect)
def on_canvas_drag(self, event):
"""Handle canvas drag for drawing selection rectangle"""
if self.selection_mode and self.selection_start:
x1, y1 = self.selection_start
x2 = self.canvas.canvasx(event.x)
y2 = self.canvas.canvasy(event.y)
if self.current_rect:
self.canvas.delete(self.current_rect)
# Draw rectangle with different colors for different modes
color = 'green' if self.selection_mode == 'key_bounds' else 'orange'
self.current_rect = self.canvas.create_rectangle(
x1, y1, x2, y2,
outline=color, width=3, dash=(5, 5)
)
def on_canvas_release(self, event):
"""Handle canvas release to finalize selection"""
if self.selection_mode and self.selection_start:
x1, y1 = self.selection_start
x2 = self.canvas.canvasx(event.x)
y2 = self.canvas.canvasy(event.y)
# Ensure we have a valid rectangle
if abs(x2 - x1) > 5 and abs(y2 - y1) > 5:
if self.selection_mode == 'key_bounds':
self.key_processor.set_manual_bounds(int(x1), int(y1), int(x2), int(y2))
self.status_label.config(text="✓ Key area selected")
# Redraw with permanent rectangle
if self.current_rect:
self.canvas.delete(self.current_rect)
self.current_rect = self.canvas.create_rectangle(
x1, y1, x2, y2,
outline='green', width=2
)
self.selection_mode = None
self.selection_status.config(text="Key area set")
elif self.selection_mode == 'groove':
self.key_processor.add_groove_region(int(x1), int(y1), int(x2), int(y2))
self.status_label.config(text=f"✓ Groove region added (Total: {len(self.key_processor.groove_regions)})")
# Draw permanent groove rectangle
if self.current_rect:
self.canvas.delete(self.current_rect)
groove_rect = self.canvas.create_rectangle(
x1, y1, x2, y2,
outline='orange', width=2, dash=(3, 3)
)
self.drawn_rectangles.append(groove_rect)
# Stay in groove mode for multiple selections
self.selection_start = None
self.current_rect = None
def _redraw_with_annotations(self):
"""Redraw image with all annotations"""
if self.key_processor.original_image:
self._display_image(self.key_processor.original_image.convert('L'))
# Redraw key bounds if set
if self.key_processor.manual_bounds:
bounds = self.key_processor.manual_bounds
self.canvas.create_rectangle(
bounds['left'], bounds['top'],
bounds['right'], bounds['bottom'],
outline='green', width=2
)
# Redraw groove regions
self.drawn_rectangles = []
for groove in self.key_processor.groove_regions:
rect = self.canvas.create_rectangle(
groove['left'], groove['top'],
groove['right'], groove['bottom'],
outline='orange', width=2, dash=(3, 3)
)
self.drawn_rectangles.append(rect)
def load_image(self):
filepath = filedialog.askopenfilename(
title="Select Key Image",
filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp *.gif"), ("All files", "*.*")]
)
if filepath:
try:
self.key_processor.load_image(filepath)
self._display_image(self.key_processor.image)
self.status_label.config(text=f"✓ Loaded: {filepath.split('/')[-1]}")
except Exception as e:
messagebox.showerror("Error", f"Failed to load image: {str(e)}")
def auto_process(self):
try:
noise_handling = self.noise_handling_var.get()
self.status_label.config(text="Processing... applying noise reduction" if noise_handling else "Processing... detecting edges")
self.root.update()
self.key_processor.auto_detect_key(noise_handling=noise_handling)
self.status_label.config(text="Processing... finding key bounds")
self.root.update()
self.key_processor.find_key_bounds()
self.status_label.config(text="Processing... extracting profile")
self.root.update()
use_top = self.edge_top_var.get()
profile = self.key_processor.extract_key_profile(use_top=use_top, consensus_window=7)
self._display_image_with_profile()
groove_info = f" (with {len(self.key_processor.groove_regions)} groove regions)" if self.key_processor.groove_regions else ""
self.status_label.config(text=f"✓ Profile extracted: {len(profile)} points{groove_info}")
except Exception as e:
messagebox.showerror("Error", f"Auto processing failed: {str(e)}")
import traceback
traceback.print_exc()
def smooth_profile(self):
try:
window = self.smooth_var.get()
if window % 2 == 0:
window += 1
self.key_processor.smooth_profile_advanced(window_size=window)
self._display_image_with_profile()
self.status_label.config(text="✓ Profile smoothed")
except Exception as e:
messagebox.showerror("Error", f"Failed to smooth: {str(e)}")
def remove_outliers(self):
try:
self.key_processor.remove_outliers()
self._display_image_with_profile()
self.status_label.config(text="✓ Outliers removed")
except Exception as e:
messagebox.showerror("Error", f"Failed to remove outliers: {str(e)}")
def generate_stl(self):
if not self.key_processor.key_profile:
messagebox.showwarning("Warning", "Please extract key profile first (Step 3)")
return
filepath = filedialog.asksaveasfilename(
title="Save STL File",
defaultextension=".stl",
filetypes=[("STL files", "*.stl"), ("All files", "*.*")]
)
if filepath:
try:
self.key_processor.generate_stl(
filepath,
depth_scale=self.depth_var.get(),
base_thickness=self.base_var.get()
)
messagebox.showinfo("Success",
f"✓ STL file generated!\n\n{filepath}\n\nYou can now scale it in your 3D slicer.")
self.status_label.config(text=f"✓ STL saved: {filepath.split('/')[-1]}")
except Exception as e:
messagebox.showerror("Error", f"Failed to generate STL: {str(e)}")
import traceback
traceback.print_exc()
def _display_image(self, image):
if image is None:
return
self.photo_image = ImageTk.PhotoImage(image)
self.canvas.delete("all")
self.canvas.create_image(0, 0, image=self.photo_image, anchor=tk.NW)
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def _display_image_with_profile(self):
if self.key_processor.processed_image and self.key_processor.key_profile:
display_img = self.key_processor.processed_image.convert('RGB')
draw = ImageDraw.Draw(display_img)
# Draw key bounds
if self.key_processor.key_bounds:
bounds = self.key_processor.key_bounds
draw.rectangle(
[bounds['left'], bounds['top'], bounds['right'], bounds['bottom']],
outline='green', width=3
)
# Draw groove regions
for groove in self.key_processor.groove_regions:
draw.rectangle(
[groove['left'], groove['top'], groove['right'], groove['bottom']],
outline='orange', width=2
)
# Draw profile
if len(self.key_processor.key_profile) > 1:
draw.line(self.key_processor.key_profile, fill='red', width=3)
for x, y in self.key_processor.key_profile[::10]:
draw.ellipse([x-2, y-2, x+2, y+2], fill='yellow', outline='red')
self._display_image(display_img)
if __name__ == "__main__":
print("Key to STL Generator - Manual Selection Tools")
print("Requirements: pip install scipy pillow numpy")
print("\nHow to use manual selection:")
print("1. Load your key image")
print("2. Click 'Select Key Area' and drag rectangle around key")
print("3. Click 'Mark Groove Region' and drag rectangles over teeth cuts")
print("4. Click 'Auto Detect Key' to process with your selections")
print("5. Refine and export STL")
root = tk.Tk()
app = KeySTLGUI(root)
root.mainloop()

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<TargetConfig Name="C:\Users\Sebastian\Desktop\INF6B\math\mandelbrot.exe" LastUsed="2025-11-06T09:04:37.9520296Z">
<EngineConfig />
<EngineOptions>
<Property name="DebugChildProcesses" value="false" />
</EngineOptions>
<TargetOptions>
<Option name="LaunchProcess">
<Property name="Executable" value="C:\Users\Sebastian\Desktop\INF6B\math\mandelbrot.exe" />
<Property name="Arguments" value="" />
</Option>
<Option name="RestoreCommandHistory">
<Property name="History">
<Property value="gn" />
<Property value="g" />
<Property value="p" />
<Property value=".restart" />
<Property value="uf mandelbrot!Render" />
<Property value="x mandelbrot!Render" />
<Property value="dv" />
<Property value="!heap" />
<Property value="x mandelbrot!*" />
<Property value="u 0x507cc0" />
<Property value="uf 0x507cc0" />
<Property value="uf mandelbrot!Complex" />
<Property value="x mandelbrot!complex" />
<Property value="x mandelbrot!complex_scale" />
<Property value="u 0x508360" />
</Property>
</Option>
<Option name="RestoreBreakpoints">
<Property name="Breakpoints">
<Property>
<Property name="Id" value="0" />
<Property name="IsEnabled" value="true" />
<Property name="OffsetExpression" value="mandelbrot!color_mandelbrot" />
<Property name="Command" value="" />
<Property name="PassCount" value="1" />
<Property name="SymbolName" value="mandelbrot!color_mandelbrot" />
<Property name="Location" value="mandelbrot!color_mandelbrot" />
<Property name="Address" value="0" />
<Property name="Module" value="mandelbrot" />
<Property name="Filename" value="C:\Users\Sebastian\Desktop\INF6B\math\mandelbrot.c" />
<Property name="Line" value="47" />
<Property name="Type" value="Software" />
</Property>
</Property>
</Option>
</TargetOptions>
</TargetConfig>

98
math/complex.c Normal file
View File

@@ -0,0 +1,98 @@
#include <stdio.h>
#include <math.h>
#include "complex.h"
// Constructors
Complex complex_new(double re, double im) {
Complex z;
z.re = re;
z.im = im;
return z;
}
// Add
Complex complex_add(Complex a, Complex b) {
return complex_new(a.re + b.re, a.im + b.im);
}
// Subtract
Complex complex_sub(Complex a, Complex b) {
return complex_new(a.re - b.re, a.im - b.im);
}
// Multiply
Complex complex_mul(Complex a, Complex b) {
return complex_new(a.re * b.re - a.im * b.im,
a.re * b.im + a.im * b.re);
}
// Divide
Complex complex_div(Complex a, Complex b) {
double denom = b.re * b.re + b.im * b.im;
return complex_new((a.re * b.re + a.im * b.im) / denom,
(a.im * b.re - a.re * b.im) / denom);
}
// Scalar multiply
Complex complex_scale(Complex a, double alpha) {
return complex_new(a.re * alpha, a.im * alpha);
}
// Conjugate
Complex complex_conjugate(Complex a) {
return complex_new(a.re, -a.im);
}
// Reciprocal
Complex complex_reciprocal(Complex a) {
double scale = a.re * a.re + a.im * a.im;
return complex_new(a.re / scale, -a.im / scale);
}
// Magnitude (faster than hypot)
double complex_abs(Complex a) {
return sqrt(a.re * a.re + a.im * a.im);
}
// Phase
double complex_phase(Complex a) {
return atan2(a.im, a.re);
}
// Exponential
Complex complex_exp(Complex a) {
double exp_re = exp(a.re);
return complex_new(exp_re * cos(a.im),
exp_re * sin(a.im));
}
// Sine
Complex complex_sin(Complex a) {
return complex_new(sin(a.re) * cosh(a.im),
cos(a.re) * sinh(a.im));
}
// Cosine
Complex complex_cos(Complex a) {
return complex_new(cos(a.re) * cosh(a.im),
-sin(a.re) * sinh(a.im));
}
// Tangent
Complex complex_tan(Complex a) {
Complex s = complex_sin(a);
Complex c = complex_cos(a);
return complex_div(s, c);
}
// Print function
void complex_print(Complex a) {
if (a.im == 0)
printf("%g", a.re);
else if (a.re == 0)
printf("%gi", a.im);
else if (a.im < 0)
printf("%g - %gi", a.re, -a.im);
else
printf("%g + %gi", a.re, a.im);
}

25
math/complex.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef COMPLEX_H
#define COMPLEX_H
typedef struct {
double re;
double im;
} Complex;
Complex complex_new(double re, double im);
Complex complex_add(Complex a, Complex b);
Complex complex_sub(Complex a, Complex b);
Complex complex_mul(Complex a, Complex b);
Complex complex_div(Complex a, Complex b);
Complex complex_scale(Complex a, double alpha);
Complex complex_conjugate(Complex a);
Complex complex_reciprocal(Complex a);
double complex_abs(Complex a);
double complex_phase(Complex a);
Complex complex_exp(Complex a);
Complex complex_sin(Complex a);
Complex complex_cos(Complex a);
Complex complex_tan(Complex a);
void complex_print(Complex a);
#endif

227
math/main.cpp Normal file
View File

@@ -0,0 +1,227 @@
#include <windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <stdio.h>
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "D3DCompiler.lib")
HWND hwnd = nullptr;
ID3D11Device* device = nullptr;
ID3D11DeviceContext* context = nullptr;
IDXGISwapChain* swapChain = nullptr;
ID3D11RenderTargetView* rtv = nullptr;
ID3D11VertexShader* vs = nullptr;
ID3D11PixelShader* ps = nullptr;
ID3D11InputLayout* inputLayout = nullptr;
ID3D11Buffer* vertexBuffer = nullptr;
// Vertex struct
struct Vertex { float x, y; };
// Vertex shader
const char* vsSrc = R"(
struct VS_INPUT { float2 pos : POSITION; };
struct PS_INPUT { float4 pos : SV_POSITION; float2 uv : TEXCOORD; };
PS_INPUT main(VS_INPUT input) {
PS_INPUT output;
output.pos = float4(input.pos,0,1);
output.uv = input.pos*0.5+0.5;
return output;
}
)";
char psSrc[2048]; // Pixel shader
// Mandelbrot view presets
struct View { float xMin, xMax, yMin, yMax; };
View views[] = {
{-2.5f, 1.0f, -1.2f, 1.2f}, // Full set
{-0.748f, -0.743f, 0.1f, 0.105f}, // Seahorse Valley
{-0.74877f, -0.74872f, 0.06505f, 0.06510f}, // Mini zoom
};
int currentView = 0;
// Forward declarations
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
bool InitWindow(HINSTANCE, int);
bool InitD3D();
void CleanUp();
bool CompileShaders();
void Render();
// Main
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) {
if (!InitWindow(hInstance, nCmdShow)) return 0;
if (!InitD3D()) return 0;
CompileShaders();
MSG msg = {};
while (msg.message != WM_QUIT) {
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
Render(); // non-blocking rendering
}
}
CleanUp();
return 0;
}
// --- Window ---
bool InitWindow(HINSTANCE hInstance, int nCmdShow) {
const char CLASS_NAME[] = "MandelbrotDX11";
WNDCLASS wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
hwnd = CreateWindowEx(0, CLASS_NAME, "DX11 Mandelbrot", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, nullptr, nullptr, hInstance, nullptr);
if (!hwnd) return false;
ShowWindow(hwnd, nCmdShow);
return true;
}
// --- WinProc ---
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY: PostQuitMessage(0); return 0;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE) PostQuitMessage(0);
if (wParam == VK_LEFT) currentView = (currentView + 2) % 3;
if (wParam == VK_RIGHT) currentView = (currentView + 1) % 3;
CompileShaders();
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
// --- D3D11 Init ---
bool InitD3D() {
DXGI_SWAP_CHAIN_DESC scd = {};
scd.BufferCount = 1;
scd.BufferDesc.Width = 800;
scd.BufferDesc.Height = 600;
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
scd.BufferDesc.RefreshRate.Numerator = 60;
scd.BufferDesc.RefreshRate.Denominator = 1;
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
scd.OutputWindow = hwnd;
scd.SampleDesc.Count = 1;
scd.Windowed = TRUE;
D3D_FEATURE_LEVEL featureLevel;
if (FAILED(D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
0, nullptr, 0, D3D11_SDK_VERSION, &scd,
&swapChain, &device, &featureLevel, &context))) return false;
ID3D11Texture2D* backBuffer = nullptr;
swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
device->CreateRenderTargetView(backBuffer, nullptr, &rtv);
backBuffer->Release();
context->OMSetRenderTargets(1, &rtv, nullptr);
D3D11_VIEWPORT vp = {};
vp.Width = 800; vp.Height = 600; vp.MinDepth = 0; vp.MaxDepth = 1;
context->RSSetViewports(1, &vp);
Vertex verts[] = { {-1,-1},{-1,1},{1,-1},{1,1} };
D3D11_BUFFER_DESC bd = {};
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(verts);
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA initData = {};
initData.pSysMem = verts;
device->CreateBuffer(&bd, &initData, &vertexBuffer);
return true;
}
// --- Compile shaders ---
bool CompileShaders() {
// Pixel shader template with smooth coloring
snprintf(psSrc, sizeof(psSrc),
R"(struct PS_INPUT { float4 pos:SV_POSITION; float2 uv: TEXCOORD; };
float mandelbrot(float2 c){
float2 z=0;
float iter=0;
const int MAX_ITER=256;
for(int i=0;i<MAX_ITER;i++){
float x=z.x*z.x - z.y*z.y + (c.x*%.8f+%.8f);
float y=2*z.x*z.y + (c.y*%.8f+%.8f);
z=float2(x,y);
if(dot(z,z)>4){iter=i; break;}
}
return iter;
}
float4 main(PS_INPUT input):SV_TARGET{
float iter=mandelbrot(input.uv);
const float MAX_ITER=256.0;
// Original color mapping (smooth)
float t = iter / MAX_ITER;
float r = clamp(9.0*(1.0-t)*t*t*t,0,1);
float g = clamp(15.0*(1.0-t)*(1.0-t)*t*t,0,1);
float b = clamp(8.5*(1.0-t)*(1.0-t)*(1.0-t)*t,0,1);
return float4(r,g,b,1.0);
})",
(views[currentView].xMax - views[currentView].xMin), views[currentView].xMin,
(views[currentView].yMax - views[currentView].yMin), views[currentView].yMin
);
ID3DBlob* vsBlob = nullptr, * psBlob = nullptr, * err = nullptr;
if (FAILED(D3DCompile(vsSrc, strlen(vsSrc), nullptr, nullptr, nullptr, "main", "vs_5_0", 0, 0, &vsBlob, &err))) {
if (err) { OutputDebugStringA((char*)err->GetBufferPointer()); err->Release(); } return false;
}
if (FAILED(D3DCompile(psSrc, strlen(psSrc), nullptr, nullptr, nullptr, "main", "ps_5_0", 0, 0, &psBlob, &err))) {
if (err) { OutputDebugStringA((char*)err->GetBufferPointer()); err->Release(); } return false;
}
if (vs) vs->Release();
if (ps) ps->Release();
if (inputLayout) inputLayout->Release();
device->CreateVertexShader(vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), nullptr, &vs);
device->CreatePixelShader(psBlob->GetBufferPointer(), psBlob->GetBufferSize(), nullptr, &ps);
D3D11_INPUT_ELEMENT_DESC layout[] = { {"POSITION",0,DXGI_FORMAT_R32G32_FLOAT,0,0,D3D11_INPUT_PER_VERTEX_DATA,0} };
device->CreateInputLayout(layout, 1, vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), &inputLayout);
vsBlob->Release(); psBlob->Release();
return true;
}
// --- Render ---
void Render() {
float clear[4] = { 0,0,0,1 };
context->ClearRenderTargetView(rtv, clear);
UINT stride = sizeof(Vertex), offset = 0;
context->IASetVertexBuffers(0, 1, &vertexBuffer, &stride, &offset);
context->IASetInputLayout(inputLayout);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
context->VSSetShader(vs, nullptr, 0);
context->PSSetShader(ps, nullptr, 0);
context->Draw(4, 0);
swapChain->Present(0, 0); // non-blocking present
}
// --- Cleanup ---
void CleanUp() {
if (vertexBuffer) vertexBuffer->Release();
if (inputLayout) inputLayout->Release();
if (vs) vs->Release();
if (ps) ps->Release();
if (rtv) rtv->Release();
if (swapChain) swapChain->Release();
if (context) context->Release();
if (device) device->Release();
}

61
math/mandelbrot.c Normal file
View File

@@ -0,0 +1,61 @@
#include "mandelbrot.h"
#include <math.h>
// Clamp helper
static int clamp(int val) {
if (val < 0) return 0;
if (val > 255) return 255;
return val;
}
// Mandelbrot iterations
int mandelbrot(Complex z0, int max_iter) {
Complex z = z0;
for (int i = 0; i < max_iter; i++) {
if (complex_abs(z) > 2.0) return i;
z = complex_add(complex_mul(z, z), z0);
}
return max_iter;
}
// Simple HSV -> RGB helper
static Color hsv_to_rgb(double h, double s, double v) {
double r = 0, g = 0, b = 0;
int i = (int)(h * 6);
double f = h * 6 - i;
double p = v * (1 - s);
double q = v * (1 - f * s);
double t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
}
Color c = { (unsigned char)clamp((int)(r * 255)),
(unsigned char)clamp((int)(g * 255)),
(unsigned char)clamp((int)(b * 255)) };
return c;
}
// Color Mandelbrot
Color color_mandelbrot(Complex z0, int max_iter) {
int iter = mandelbrot(z0, max_iter);
if (iter == max_iter)
return (Color) { 0, 0, 0 }; // black inside the set
// Map iteration to color hue
double t = (double)iter / max_iter;
double hue = 0.95 + 10 * t; // arbitrary scaling for more variation
hue = fmod(hue, 1.0);
double sat = 0.6; // saturation
double val = 1.0; // brightness
return hsv_to_rgb(hue, sat, val);
}

12
math/mandelbrot.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef MANDELBROT_H
#define MANDELBROT_H
#include "complex.h"
typedef struct { unsigned char r, g, b; } Color;
int mandelbrot(Complex z0, int max_iter);
Color grey_mandelbrot(Complex z0, int max_iter); // optional, for backward compatibility
Color color_mandelbrot(Complex z0, int max_iter);
#endif

24
math/mandelbrot.hlsl Normal file
View File

@@ -0,0 +1,24 @@
cbuffer ViewportBuffer : register(b0)
{
float xmin;
float xmax;
float ymin;
float ymax;
int max_iter;
};
struct PS_IN { float4 pos : SV_POSITION; };
float4 PSMain(PS_IN input) : SV_TARGET
{
float2 uv = input.pos.xy * 0.5 + 0.5; // [-1,1] -> [0,1]
float2 c = float2(xmin,ymin) + uv * float2(xmax-xmin, ymax-ymin);
float2 z = c;
int i;
for(i=0;i<max_iter;i++){
if(dot(z,z)>4) break;
z = float2(z.x*z.x - z.y*z.y, 2*z.x*z.y)+c;
}
float t = i/(float)max_iter;
return float4(t,0.5*t,1.0-t,1);
}

2403
niacin/compiler.py Normal file

File diff suppressed because it is too large Load Diff

21
niacin/cpp/Makefile Normal file
View File

@@ -0,0 +1,21 @@
CC = gcc
CFLAGS = -Wall -Wextra -Werror -O2 -std=c99 -D_DEFAULT_SOURCE
CFLAGS += -fstack-protector-strong -D_FORTIFY_SOURCE=2
LDFLAGS = -z relro -z now
SOURCES = main.c vm_core.c
OBJECTS = $(SOURCES:.c=.o)
TARGET = popvm
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) $(LDFLAGS) -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJECTS) $(TARGET)
.PHONY: all clean

76
niacin/cpp/main.c Normal file
View File

@@ -0,0 +1,76 @@
#include "vm_types.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Safe file reading
static uint8_t* read_file(const char* filename, size_t* size) {
FILE* file = fopen(filename, "rb");
if (!file) {
fprintf(stderr, "Cannot open file: %s\n", filename);
return NULL;
}
fseek(file, 0, SEEK_END);
long file_size = ftell(file);
fseek(file, 0, SEEK_SET);
if (file_size <= 0 || file_size > 10 * 1024 * 1024) { // 10MB limit
fclose(file);
fprintf(stderr, "Invalid file size\n");
return NULL;
}
uint8_t* buffer = malloc(file_size);
if (!buffer) {
fclose(file);
fprintf(stderr, "Memory allocation failed\n");
return NULL;
}
if (fread(buffer, 1, file_size, file) != (size_t)file_size) {
fclose(file);
free(buffer);
fprintf(stderr, "File read error\n");
return NULL;
}
fclose(file);
*size = file_size;
return buffer;
}
int main(int argc, char* argv[]) {
if (argc != 2) {
printf("Usage: %s <file.popclass>\n", argv[0]);
return 1;
}
size_t file_size;
uint8_t* bytecode = read_file(argv[1], &file_size);
if (!bytecode) {
return 1;
}
vm_t* vm = vm_create(bytecode, file_size);
if (!vm) {
fprintf(stderr, "Failed to create VM\n");
free(bytecode);
return 1;
}
if (!vm_validate_bytecode(vm)) {
fprintf(stderr, "Invalid bytecode file\n");
vm_destroy(vm);
free(bytecode);
return 1;
}
printf("Executing program...\n");
vm_execute(vm);
printf("Program finished\n");
vm_destroy(vm);
free(bytecode);
return 0;
}

BIN
niacin/cpp/sample.popclass Normal file

Binary file not shown.

285
niacin/cpp/vm_core.c Normal file
View File

@@ -0,0 +1,285 @@
#include "vm_types.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
// Safe memory allocation with overflow checking
static void* safe_malloc(size_t size) {
if (size == 0 || size > SIZE_MAX / 2) return NULL;
void* ptr = malloc(size);
if (!ptr) {
fprintf(stderr, "Memory allocation failed\n");
exit(EXIT_FAILURE);
}
return ptr;
}
static void* safe_realloc(void* ptr, size_t size) {
if (size == 0 || size > SIZE_MAX / 2) return NULL;
void* new_ptr = realloc(ptr, size);
if (!new_ptr) {
fprintf(stderr, "Memory reallocation failed\n");
exit(EXIT_FAILURE);
}
return new_ptr;
}
// Initialize VM with security limits
vm_t* vm_create(uint8_t* bytecode, size_t size) {
vm_t* vm = safe_malloc(sizeof(vm_t));
memset(vm, 0, sizeof(vm_t));
vm->bytecode = bytecode;
vm->bytecode_size = size;
vm->ip = 0;
vm->halted = false;
// Security limits
vm->max_stack_size = 65536;
vm->max_call_depth = 256;
vm->current_call_depth = 0;
return vm;
}
// Safe bytecode reading with bounds checking
static bool read_u8(vm_t* vm, uint8_t* result) {
if (vm->ip >= vm->bytecode_size) return false;
*result = vm->bytecode[vm->ip++];
return true;
}
static bool read_u16(vm_t* vm, uint16_t* result) {
if (vm->ip + 2 > vm->bytecode_size) return false;
memcpy(result, &vm->bytecode[vm->ip], 2);
vm->ip += 2;
return true;
}
static bool read_u32(vm_t* vm, uint32_t* result) {
if (vm->ip + 4 > vm->bytecode_size) return false;
memcpy(result, &vm->bytecode[vm->ip], 4);
vm->ip += 4;
return true;
}
static bool read_i32(vm_t* vm, int32_t* result) {
if (vm->ip + 4 > vm->bytecode_size) return false;
memcpy(result, &vm->bytecode[vm->ip], 4);
vm->ip += 4;
return true;
}
static bool read_f32(vm_t* vm, float* result) {
if (vm->ip + 4 > vm->bytecode_size) return false;
memcpy(result, &vm->bytecode[vm->ip], 4);
vm->ip += 4;
return true;
}
// Stack operations with bounds checking
static bool stack_push(frame_t* frame, value_t value) {
if (frame->stack_size >= frame->stack_capacity) {
size_t new_capacity = frame->stack_capacity * 2;
if (new_capacity == 0) new_capacity = 16;
value_t* new_stack = safe_realloc(frame->stack, new_capacity * sizeof(value_t));
if (!new_stack) return false;
frame->stack = new_stack;
frame->stack_capacity = new_capacity;
}
frame->stack[frame->stack_size++] = value;
return true;
}
static bool stack_pop(frame_t* frame, value_t* result) {
if (frame->stack_size == 0) return false;
*result = frame->stack[--frame->stack_size];
return true;
}
// Type checking and conversion
static bool type_check_binary(value_t a, value_t b, type_code_t* result_type) {
// Simple type checking - in real implementation, add proper type promotion
if (a.type == b.type) {
*result_type = a.type;
return true;
}
return false;
}
static int32_t value_to_int(value_t val) {
switch (val.type) {
case TYPE_I8: return val.data.i8;
case TYPE_U8: return val.data.u8;
case TYPE_I16: return val.data.i16;
case TYPE_U16: return val.data.u16;
case TYPE_I32: return val.data.i32;
case TYPE_U32: return (int32_t)val.data.u32;
case TYPE_BOOL: return val.data.boolean ? 1 : 0;
case TYPE_CHAR: return (int32_t)val.data.character;
default: return 0;
}
}
static float value_to_float(value_t val) {
switch (val.type) {
case TYPE_F32: return val.data.f32;
case TYPE_I32: return (float)val.data.i32;
case TYPE_U32: return (float)val.data.u32;
default: return 0.0f;
}
}
// Bytecode validation
bool vm_validate_bytecode(vm_t* vm) {
// Check magic number
if (vm->bytecode_size < 4 || memcmp(vm->bytecode, "POPC", 4) != 0) {
return false;
}
// Add more validation as needed
return true;
}
// Execute single instruction with full error checking
bool vm_execute_instruction(vm_t* vm) {
if (vm->halted || vm->ip >= vm->bytecode_size) return false;
uint8_t opcode;
if (!read_u8(vm, &opcode)) return false;
frame_t* frame = vm->current_frame;
if (!frame) return false;
switch (opcode) {
case OP_PUSH_CONST: {
uint32_t const_idx;
if (!read_u32(vm, &const_idx) || const_idx >= vm->constants_count) {
return false;
}
if (!stack_push(frame, vm->constants[const_idx])) return false;
break;
}
case OP_PUSH_INT: {
uint8_t width;
int32_t value;
if (!read_u8(vm, &width) || !read_i32(vm, &value)) return false;
value_t val = { 0 };
switch (width) {
case 8: val.type = TYPE_I8; val.data.i8 = (int8_t)value; break;
case 16: val.type = TYPE_I16; val.data.i16 = (int16_t)value; break;
case 32: val.type = TYPE_I32; val.data.i32 = value; break;
default: return false;
}
if (!stack_push(frame, val)) return false;
break;
}
case OP_ADD: {
value_t a, b;
if (!stack_pop(frame, &a) || !stack_pop(frame, &b)) return false;
type_code_t result_type;
if (!type_check_binary(a, b, &result_type)) return false;
value_t result = { 0 };
result.type = result_type;
if (result_type == TYPE_F32) {
result.data.f32 = value_to_float(b) + value_to_float(a);
}
else {
result.data.i32 = value_to_int(b) + value_to_int(a);
}
if (!stack_push(frame, result)) return false;
break;
}
case OP_CMP_EQ: {
value_t a, b;
if (!stack_pop(frame, &a) || !stack_pop(frame, &b)) return false;
value_t result = { 0 };
result.type = TYPE_BOOL;
result.data.boolean = (value_to_int(a) == value_to_int(b));
if (!stack_push(frame, result)) return false;
break;
}
case OP_JMP: {
int32_t offset;
if (!read_i32(vm, &offset)) return false;
// Check for negative jumps (security)
if (offset < 0 && (size_t)(-offset) > vm->ip) return false;
if (vm->ip + offset >= vm->bytecode_size) return false;
vm->ip += offset;
break;
}
case OP_PRINT: {
value_t val;
if (!stack_pop(frame, &val)) return false;
switch (val.type) {
case TYPE_I32: printf("%d\n", val.data.i32); break;
case TYPE_F32: printf("%f\n", val.data.f32); break;
case TYPE_BOOL: printf("%s\n", val.data.boolean ? "true" : "false"); break;
case TYPE_STR: printf("%s\n", val.data.string); break;
default: printf("<unknown>\n"); break;
}
break;
}
case OP_HALT:
vm->halted = true;
break;
default:
fprintf(stderr, "Unknown opcode: 0x%02x\n", opcode);
return false;
}
return true;
}
// Main execution loop
void vm_execute(vm_t* vm) {
while (!vm->halted && vm_execute_instruction(vm)) {
// Continue execution
}
}
// Cleanup
void vm_destroy(vm_t* vm) {
if (!vm) return;
// Free constants
for (size_t i = 0; i < vm->constants_count; i++) {
if (vm->constants[i].type == TYPE_STR && vm->constants[i].data.string) {
free(vm->constants[i].data.string);
}
}
free(vm->constants);
// Free functions
free(vm->functions);
// Free frames (simplified - in real impl, walk call stack)
if (vm->current_frame) {
free(vm->current_frame->locals);
free(vm->current_frame->stack);
free(vm->current_frame);
}
free(vm);
}

120
niacin/cpp/vm_types.h Normal file
View File

@@ -0,0 +1,120 @@
#ifndef VM_TYPES_H
#define VM_TYPES_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
// Type codes
typedef enum {
TYPE_I8 = 0x01,
TYPE_U8 = 0x02,
TYPE_I16 = 0x03,
TYPE_U16 = 0x04,
TYPE_I32 = 0x05,
TYPE_U32 = 0x06,
TYPE_F32 = 0x07,
TYPE_BOOL = 0x08,
TYPE_CHAR = 0x09,
TYPE_STR = 0x0A
} type_code_t;
// Opcodes
typedef enum {
OP_PUSH_CONST = 0x01,
OP_PUSH_INT = 0x02,
OP_PUSH_FLOAT = 0x03,
OP_PUSH_STR = 0x04,
OP_LOAD_LOCAL = 0x10,
OP_STORE_LOCAL = 0x11,
OP_ADD = 0x20,
OP_SUB = 0x21,
OP_MUL = 0x22,
OP_DIV = 0x23,
OP_MOD = 0x24,
OP_CMP_EQ = 0x40,
OP_CMP_NEQ = 0x41,
OP_CMP_LT = 0x42,
OP_CMP_GT = 0x43,
OP_CMP_LE = 0x44,
OP_CMP_GE = 0x45,
OP_JMP = 0x50,
OP_JMP_IF = 0x51,
OP_JMP_IF_NOT = 0x52,
OP_CALL = 0x60,
OP_RET = 0x61,
OP_DUP = 0x80,
OP_POP = 0x81,
OP_PRINT = 0x90,
OP_HALT = 0xA0
} opcode_t;
// Value union with type safety
typedef union {
int8_t i8;
uint8_t u8;
int16_t i16;
uint16_t u16;
int32_t i32;
uint32_t u32;
float f32;
bool boolean;
char character;
char* string;
} value_data_t;
typedef struct {
type_code_t type;
value_data_t data;
} value_t;
// Function definition
typedef struct {
uint32_t name_index;
uint8_t arg_count;
uint8_t local_count;
uint16_t reserved;
uint32_t code_size;
uint32_t code_offset;
} function_t;
// Call frame
typedef struct frame {
struct frame* prev;
uint32_t return_ip;
value_t* locals;
value_t* stack;
size_t stack_size;
size_t stack_capacity;
size_t locals_count;
} frame_t;
// Virtual machine state
typedef struct {
uint8_t* bytecode;
size_t bytecode_size;
uint32_t ip;
value_t* constants;
size_t constants_count;
function_t* functions;
size_t functions_count;
frame_t* current_frame;
bool halted;
// Security settings
size_t max_stack_size;
size_t max_call_depth;
size_t current_call_depth;
} vm_t;
// Function declarations
vm_t* vm_create(uint8_t* bytecode, size_t size);
bool vm_validate_bytecode(vm_t* vm);
bool vm_execute_instruction(vm_t* vm);
void vm_execute(vm_t* vm);
void vm_destroy(vm_t* vm);
#endif

16
niacin/sample.popasm Normal file
View File

@@ -0,0 +1,16 @@
; POP Class File Version 1.0
; Magic: b'POPC'
; Constant Pool
; Count: 0
; Function Table
; Count: 1
; func[0]: func_0, args=0, locals=1, code_size=15, offset=0x00000024
; Function 0
0x00000024: PUSH_INT i32 12
0x0000002a: STORE_LOCAL local[0]
0x0000002d: LOAD_LOCAL local[0]
0x00000030: PRINT
0x00000031: RET 0

BIN
niacin/sample.popclass Normal file

Binary file not shown.

4
niacin/sample.src Normal file
View File

@@ -0,0 +1,4 @@
fun main() {
u8 testint = 255;
print(testint);
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<TargetConfig Name="C:\Users\Sebastian\Desktop\INF6B\simulations\donut.c\donut.exe" LastUsed="2025-11-07T20:28:26.2310451Z">
<EngineConfig />
<EngineOptions>
<Property name="DebugChildProcesses" value="false" />
</EngineOptions>
<TargetOptions>
<Option name="LaunchProcess">
<Property name="Executable" value="C:\Users\Sebastian\Desktop\INF6B\simulations\donut.c\donut.exe" />
<Property name="Arguments" value="" />
</Option>
</TargetOptions>
</TargetConfig>