-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdeploy_processing_h.py
More file actions
36 lines (30 loc) · 220 KB
/
Copy pathdeploy_processing_h.py
File metadata and controls
36 lines (30 loc) · 220 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/env python3
"""
Deploys the fixed Processing.h -- adds Windows macro undef block after the
GL includes so OPAQUE/DELETE/CLOSE/DIFFERENCE/etc. from wingdi.h don't
clobber Processing constants on MSYS2/MinGW.
No-op on Linux/macOS. No jar rebuild needed.
Run from anywhere inside the CppMode checkout.
"""
import os, sys
def find_root():
for start in [os.getcwd(), os.path.dirname(os.path.abspath(__file__))]:
c = os.path.abspath(start)
for _ in range(6):
if os.path.isfile(os.path.join(c, "mode.properties")): return c
p = os.path.dirname(c)
if p == c: break
c = p
print("ERROR: mode.properties not found."); sys.exit(1)
CONTENT = '#pragma once\n#if __has_include("stb_truetype.h") && !defined(PROCESSING_HAS_STB_TRUETYPE)\n# define PROCESSING_HAS_STB_TRUETYPE 1\n#endif\n#if PROCESSING_HAS_STB_TRUETYPE\n// Include stb_truetype header-only (no implementation) for type definitions\n# ifndef STB_TRUETYPE_IMPLEMENTATION\n# include "stb_truetype.h"\n# endif\n#endif\n#ifndef _WIN32\n#include <dirent.h>\n#endif\n#include <functional>\n// =============================================================================\n// Processing.h -- processing-cpp API\n// =============================================================================\n// processing-cpp is a C++ creative coding framework inspired by Processing (Java).\n// It exposes a familiar draw-loop API backed by OpenGL/GLFW/GLEW.\n//\n// HOW TO USE:\n// 1. Include this header in your sketch file.\n// 2. Inside namespace Processing { ... } define:\n// void setup() { size(640,360); }\n// void draw() { background(0); ellipse(mouseX,mouseY,40,40); }\n// 3. Compile with Processing.cpp and link against GLFW + GLEW + OpenGL.\n//\n// FILE STRUCTURE:\n// Processing.h -- This file. API declarations, inline helpers, classes.\n// Processing.cpp -- Implementation of all declared functions.\n// Platform.h -- OS abstraction (file dialogs, serial, process, sleep).\n// IDE.cpp -- The processing-cpp IDE (sketch editor, build, run, terminal).\n// =============================================================================\n\n// ---------------------------------------------------------------------------\n// Platform shim (must come first; provides termios/glob stubs on Windows)\n// ---------------------------------------------------------------------------\n#if __has_include("Platform.h")\n# include "Platform.h"\n#endif\n\n// ---------------------------------------------------------------------------\n// M_PI: Windows (MinGW) only defines this when _USE_MATH_DEFINES is set\n// before including <cmath>. We also provide a fallback just in case.\n// ---------------------------------------------------------------------------\n#ifndef _USE_MATH_DEFINES\n# define _USE_MATH_DEFINES\n#endif\n#include <cmath>\n#ifndef M_PI\n# define M_PI 3.14159265358979323846\n#endif\n\n// ---------------------------------------------------------------------------\n// Standard library includes\n// ---------------------------------------------------------------------------\n#include <iostream>\n#include <sstream>\n#include <fstream>\n#include <regex>\n#include <cstdlib>\n#include <cstdarg>\n#include <ctime>\n#include <chrono>\n#include <string>\n#include <vector>\n#include <thread>\n#include <functional>\n#include <algorithm>\n#include <memory>\n#include <map>\n#include <set>\n#include <random>\n#include <stack>\n#include <queue>\n#include <list>\n#include <deque>\n#include <tuple>\n#include <optional>\n#include <variant>\n#include <numeric>\n#include <iterator>\n#include <memory>\n#include <regex>\n#include <iomanip>\n#include <unordered_map>\n#include <unordered_set>\n// ---------------------------------------------------------------------------\n// OpenGL / GLFW\n// ---------------------------------------------------------------------------\n// Java-style string + number concatenation\ninline std::string operator+(const std::string& s, int n) { return s + std::to_string(n); }\ninline std::string operator+(const std::string& s, long n) { return s + std::to_string(n); }\ninline std::string operator+(const std::string& s, size_t n) { return s + std::to_string(n); }\ninline std::string operator+(const std::string& s, float n) { return s + std::to_string(n); }\ninline std::string operator+(const std::string& s, double n) { return s + std::to_string(n); }\ninline std::string operator+(const std::string& s, char c) { return s + std::string(1, c); }\ninline std::string operator+(int n, const std::string& s) { return std::to_string(n) + s; }\ninline std::string operator+(long n, const std::string& s) { return std::to_string(n) + s; }\ninline std::string operator+(size_t n, const std::string& s) { return std::to_string(n) + s; }\ninline std::string operator+(float n, const std::string& s) { return std::to_string(n) + s; }\ninline std::string operator+(double n, const std::string& s) { return std::to_string(n) + s; }\ninline std::string operator+(char c, const std::string& s) { return std::string(1, c) + s; }\n#include <GL/glew.h>\n#include <GLFW/glfw3.h>\n\n// =============================================================================\n// WINDOWS MACRO CLEANUP\n// =============================================================================\n// Root cause: on Windows/MSYS2/MinGW, <GL/glew.h> includes <windows.h> which\n// pulls in <wingdi.h>. That header defines macros like OPAQUE, TRANSPARENT,\n// DELETE, CLOSE, DIFFERENCE, BLEND, ADD, MULTIPLY, SCREEN, GRAY, INVERT, etc.\n// as plain integer preprocessor macros. Later in this file we define Processing\n// constants with those same names as "static constexpr int OPAQUE = 3;" -- but\n// the macro fires first and turns that into "static constexpr int 2 = 3;" which\n// is a syntax error ("expected unqualified-id before numeric constant").\n//\n// WIN32_LEAN_AND_MEAN doesn\'t help because GLEW needs wingdi.h for its own GL\n// type definitions. The only correct fix is to #undef the offending macros\n// after the includes that caused them, before our own definitions use the names.\n// Each undef is inside #ifdef so it is a complete no-op on Linux and macOS.\n#ifdef OPAQUE\n# undef OPAQUE\n#endif\n#ifdef TRANSPARENT\n# undef TRANSPARENT\n#endif\n#ifdef ALTERNATE\n# undef ALTERNATE\n#endif\n#ifdef WINDING\n# undef WINDING\n#endif\n#ifdef RELATIVE\n# undef RELATIVE\n#endif\n#ifdef ABSOLUTE\n# undef ABSOLUTE\n#endif\n#ifdef CLOSE\n# undef CLOSE\n#endif\n#ifdef DELETE\n# undef DELETE\n#endif\n#ifdef DIFFERENCE\n# undef DIFFERENCE\n#endif\n#ifdef BLEND\n# undef BLEND\n#endif\n#ifdef ADD\n# undef ADD\n#endif\n#ifdef SUBTRACT\n# undef SUBTRACT\n#endif\n#ifdef MULTIPLY\n# undef MULTIPLY\n#endif\n#ifdef SCREEN\n# undef SCREEN\n#endif\n#ifdef OVERLAY\n# undef OVERLAY\n#endif\n#ifdef DARKEST\n# undef DARKEST\n#endif\n#ifdef LIGHTEST\n# undef LIGHTEST\n#endif\n#ifdef INVERT\n# undef INVERT\n#endif\n#ifdef GRAY\n# undef GRAY\n#endif\n#ifdef CROSS\n# undef CROSS\n#endif\n#ifdef ARROW\n# undef ARROW\n#endif\n#ifdef HAND\n# undef HAND\n#endif\n#ifdef MOVE\n# undef MOVE\n#endif\n#ifdef WAIT\n# undef WAIT\n#endif\n#ifdef ERROR\n# undef ERROR\n#endif\n#ifdef NEAR\n# undef NEAR\n#endif\n#ifdef FAR\n# undef FAR\n#endif\n\n// =============================================================================\n// DEBUG OUTPUT -- toggle with -DPROCESSING_DEBUG at compile time\n// =============================================================================\n// Use PDEBUG(...) anywhere you\'d normally reach for a raw fprintf(stderr,...)\n// call while investigating something. It\'s a no-op (compiles to nothing,\n// zero runtime cost) unless PROCESSING_DEBUG is defined, so debug prints\n// can be left in the source permanently without ever reaching a normal\n// build or a user\'s console -- no more hunting down and deleting stray\n// fprintf calls by hand once an investigation is done.\n//\n// Usage (same argument style as fprintf, always include the trailing \\n):\n// PDEBUG("beginDraw: width=%d height=%d\\n", width, height);\n//\n// To actually see the output during local debugging, rebuild with:\n// g++ -DPROCESSING_DEBUG ... (alongside the other -D flags already used)\n#ifndef PROCESSING_BUILD_STAMP\n // Fallback for any compile that doesn\'t go through rebuild-engine.sh\n // (e.g. the IDE\'s own per-sketch compile, which links the pre-built\n // Processing.o but never defines this itself). Seeing "UNKNOWN" at\n // runtime is itself a useful signal that something bypassed the\n // normal engine-build script.\n #define PROCESSING_BUILD_STAMP "UNKNOWN"\n#endif\n#ifndef PROCESSING_WEBSITE_URL\n // Fallback only -- the REAL value always comes from\n // config/cppmode.properties\'s website.base.url, read fresh by\n // rebuild-engine.sh and passed in via -DPROCESSING_WEBSITE_URL at\n // build time. Nothing in this source file ever hardcodes the actual\n // URL string itself; this fallback only exists so a compile that\n // bypasses the script entirely still produces a valid (if generic)\n // message instead of a broken one.\n #define PROCESSING_WEBSITE_URL "https://processing-cpp.github.io"\n#endif\n\n#ifdef PROCESSING_DEBUG\n #define PDEBUG(...) fprintf(stderr, "[PDEBUG] " __VA_ARGS__)\n#else\n #define PDEBUG(...) do {} while (0)\n#endif\n\n\nnamespace Processing {\n\n// =============================================================================\n// PVECTOR -- 2D/3D vector with all standard Processing operations\n// =============================================================================\n\nclass PVector {\npublic:\n float x, y, z;\n\n // Constructors\n PVector() : x(0), y(0), z(0) {}\n // Accept any arithmetic type (int, float, double) to match Java\'s implicit\n // widening -- eliminates narrowing-conversion warnings from expressions like\n // PVector(width/2, height/2) where width/height are int.\n template<typename A, typename B,\n typename = std::enable_if_t<std::is_arithmetic_v<A> && std::is_arithmetic_v<B>>>\n PVector(A x, B y) : x((float)x), y((float)y), z(0) {}\n template<typename A, typename B, typename C,\n typename = std::enable_if_t<std::is_arithmetic_v<A> && std::is_arithmetic_v<B> && std::is_arithmetic_v<C>>>\n PVector(A x, B y, C z) : x((float)x), y((float)y), z((float)z) {}\n\n // Setters\n PVector& set(float _x, float _y, float _z=0) { x=_x; y=_y; z=_z; return *this; }\n PVector& set(const PVector& v) { x=v.x; y=v.y; z=v.z; return *this; }\n PVector copy() const { return PVector(x, y, z); }\n\n // Magnitude\n float mag() const { return std::sqrt(x*x + y*y + z*z); }\n float magSq() const { return x*x + y*y + z*z; }\n\n // Arithmetic (in-place)\n PVector& add(float _x, float _y, float _z=0) { x+=_x; y+=_y; z+=_z; return *this; }\n PVector& add(const PVector& v) { x+=v.x; y+=v.y; z+=v.z; return *this; }\n PVector& sub(float _x, float _y, float _z=0) { x-=_x; y-=_y; z-=_z; return *this; }\n PVector& sub(const PVector& v) { x-=v.x; y-=v.y; z-=v.z; return *this; }\n PVector& mult(float s) { x*=s; y*=s; z*=s; return *this; }\n PVector& div(float s) { x/=s; y/=s; z/=s; return *this; }\n\n // Arithmetic (static, returns new vector)\n static PVector add(const PVector& a, const PVector& b) { return PVector(a.x+b.x, a.y+b.y, a.z+b.z); }\n static PVector sub(const PVector& a, const PVector& b) { return PVector(a.x-b.x, a.y-b.y, a.z-b.z); }\n static PVector mult(const PVector& v, float s) { return PVector(v.x*s, v.y*s, v.z*s); }\n static PVector div(const PVector& v, float s) { return PVector(v.x/s, v.y/s, v.z/s); }\n\n // Operators\n PVector operator+(const PVector& v) const { return PVector(x+v.x, y+v.y, z+v.z); }\n PVector operator-(const PVector& v) const { return PVector(x-v.x, y-v.y, z-v.z); }\n PVector operator*(float s) const { return PVector(x*s, y*s, z*s); }\n PVector operator/(float s) const { return PVector(x/s, y/s, z/s); }\n PVector& operator+=(const PVector& v) { return add(v); }\n PVector& operator-=(const PVector& v) { return sub(v); }\n PVector& operator*=(float s) { return mult(s); }\n PVector& operator/=(float s) { return div(s); }\n bool operator==(const PVector& v) const { return x==v.x && y==v.y && z==v.z; }\n bool operator!=(const PVector& v) const { return !(*this==v); }\n\n // Dot / cross product\n float dot(const PVector& v) const { return x*v.x + y*v.y + z*v.z; }\n float dot(float _x, float _y, float _z=0) const { return x*_x + y*_y + z*_z; }\n static float dot(const PVector& a, const PVector& b) { return a.dot(b); }\n PVector cross(const PVector& v) const { return PVector(y*v.z-z*v.y, z*v.x-x*v.z, x*v.y-y*v.x); }\n static PVector cross(const PVector& a, const PVector& b) { return a.cross(b); }\n\n // Normalization / limits\n PVector& normalize() { float m=mag(); if(m>0) div(m); return *this; }\n PVector normalized() const { PVector v(*this); return v.normalize(); }\n PVector& limit(float mx) { if(magSq()>mx*mx){ normalize(); mult(mx); } return *this; }\n PVector& setMag(float m) { normalize(); mult(m); return *this; }\n\n // Distance / angle\n float dist(const PVector& v) const { float dx=x-v.x,dy=y-v.y,dz=z-v.z; return std::sqrt(dx*dx+dy*dy+dz*dz); }\n static float dist(const PVector& a, const PVector& b) { return a.dist(b); }\n float heading() const { return std::atan2(y, x); }\n // heading2D() is @Deprecated in Processing 4 Java but still present as an\n // alias -- keep it here so sketches copied from old examples just work.\n float heading2D() const { return heading(); }\n float angleBetween(const PVector& v) const {\n float m = mag() * v.mag();\n if (m == 0) return 0;\n float c = dot(v) / m;\n c = c < -1 ? -1 : (c > 1 ? 1 : c);\n return std::acos(c);\n }\n static float angleBetween(const PVector& a, const PVector& b) { return a.angleBetween(b); }\n\n // Mutation\n PVector& rotate(float t) { float c=std::cos(t),s=std::sin(t),nx=x*c-y*s,ny=x*s+y*c; x=nx; y=ny; return *this; }\n PVector& lerp(const PVector& v, float t) { x+=(v.x-x)*t; y+=(v.y-y)*t; z+=(v.z-z)*t; return *this; }\n PVector& lerp(float _x, float _y, float _z, float t) { x+=(_x-x)*t; y+=(_y-y)*t; z+=(_z-z)*t; return *this; }\n static PVector lerp(const PVector& a, const PVector& b, float t) {\n return PVector(a.x+(b.x-a.x)*t, a.y+(b.y-a.y)*t, a.z+(b.z-a.z)*t);\n }\n\n // Static constructors\n static PVector fromAngle(float a, float len=1.0f) { return PVector(std::cos(a)*len, std::sin(a)*len); }\n static PVector random2D() {\n float a = static_cast<float>(rand()) / RAND_MAX * 6.28318f;\n return fromAngle(a);\n }\n static PVector random3D() {\n float t = static_cast<float>(rand()) / RAND_MAX * 6.28318f;\n float p = std::acos(2.0f * static_cast<float>(rand()) / RAND_MAX - 1.0f);\n return PVector(std::sin(p)*std::cos(t), std::sin(p)*std::sin(t), std::cos(p));\n }\n\n std::string toString() const {\n std::ostringstream ss;\n ss << "[ " << x << ", " << y << ", " << z << " ]";\n return ss.str();\n }\n};\n\n// =============================================================================\n// PCOLOR -- RGBA color with HSB conversion and blend operations\n// =============================================================================\n\nclass PColor {\npublic:\n float r, g, b, a;\n\n PColor() : r(0), g(0), b(0), a(255) {}\n PColor(float gray) : r(gray),g(gray),b(gray),a(255) {}\n PColor(float gray, float a) : r(gray),g(gray),b(gray),a(a) {}\n PColor(float r, float g, float b) : r(r), g(g), b(b), a(255) {}\n PColor(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) {}\n\n // Construct from packed ARGB integer (0xAARRGGBB)\n explicit PColor(unsigned int argb)\n : r((argb>>16)&0xFF), g((argb>>8)&0xFF), b(argb&0xFF), a((argb>>24)&0xFF) {}\n\n // Pack to ARGB integer\n unsigned int toARGB() const {\n int ri=(int)std::fmax(0,std::fmin(255,r));\n int gi=(int)std::fmax(0,std::fmin(255,g));\n int bi=(int)std::fmax(0,std::fmin(255,b));\n int ai=(int)std::fmax(0,std::fmin(255,a));\n return (unsigned int)((ai<<24)|(ri<<16)|(gi<<8)|bi);\n }\n\n // Normalised [0..1] accessors\n float rf() const { return r/255.0f; }\n float gf() const { return g/255.0f; }\n float bf() const { return b/255.0f; }\n float af() const { return a/255.0f; }\n\n PColor& set(float _r, float _g, float _b, float _a=255) { r=_r; g=_g; b=_b; a=_a; return *this; }\n PColor& set(float gray, float _a=255) { r=g=b=gray; a=_a; return *this; }\n PColor copy() const { return PColor(r, g, b, a); }\n\n // HSB conversions\n float hue() const {\n float rf_=r/255.f, gf_=g/255.f, bf_=b/255.f;\n float mx=std::fmax(rf_,std::fmax(gf_,bf_));\n float mn=std::fmin(rf_,std::fmin(gf_,bf_));\n float d=mx-mn;\n if (d==0) return 0;\n float h = (mx==rf_) ? (gf_-bf_)/d : (mx==gf_) ? 2.f+(bf_-rf_)/d : 4.f+(rf_-gf_)/d;\n h *= 60.f;\n if (h < 0) h += 360.f;\n return h;\n }\n float saturation() const {\n float mx=std::fmax(r,std::fmax(g,b));\n float mn=std::fmin(r,std::fmin(g,b));\n return mx==0 ? 0 : ((mx-mn)/mx)*100.f;\n }\n float brightness() const { return std::fmax(r,std::fmax(g,b))/255.f*100.f; }\n\n static PColor fromHSB(float h, float s, float bv, float a=255) {\n s /= 100.f; bv /= 100.f;\n if (s == 0) { float v=bv*255.f; return PColor(v,v,v,a); }\n float hh=std::fmod(h,360.f)/60.f;\n int i=(int)hh;\n float f=hh-i, p=bv*(1-s), q=bv*(1-s*f), t=bv*(1-s*(1-f));\n float rv,gv,blv;\n switch(i){\n case 0: rv=bv;gv=t; blv=p; break;\n case 1: rv=q; gv=bv;blv=p; break;\n case 2: rv=p; gv=bv;blv=t; break;\n case 3: rv=p; gv=q; blv=bv; break;\n case 4: rv=t; gv=p; blv=bv; break;\n default:rv=bv;gv=p; blv=q; break;\n }\n return PColor(rv*255,gv*255,blv*255,a);\n }\n\n // Arithmetic operators\n PColor operator+(const PColor& o) const { return PColor(r+o.r, g+o.g, b+o.b, a+o.a); }\n PColor operator-(const PColor& o) const { return PColor(r-o.r, g-o.g, b-o.b, a-o.a); }\n PColor operator*(float s) const { return PColor(r*s, g*s, b*s, a*s); }\n PColor operator/(float s) const { return PColor(r/s, g/s, b/s, a/s); }\n PColor& operator+=(const PColor& o) { r+=o.r; g+=o.g; b+=o.b; a+=o.a; return *this; }\n PColor& operator-=(const PColor& o) { r-=o.r; g-=o.g; b-=o.b; a-=o.a; return *this; }\n PColor& operator*=(float s) { r*=s; g*=s; b*=s; a*=s; return *this; }\n PColor& operator/=(float s) { r/=s; g/=s; b/=s; a/=s; return *this; }\n bool operator==(const PColor& o) const { return r==o.r && g==o.g && b==o.b && a==o.a; }\n bool operator!=(const PColor& o) const { return !(*this==o); }\n\n // Utility\n static PColor lerp(const PColor& c1, const PColor& c2, float t) {\n return PColor(c1.r+(c2.r-c1.r)*t, c1.g+(c2.g-c1.g)*t, c1.b+(c2.b-c1.b)*t, c1.a+(c2.a-c1.a)*t);\n }\n PColor& clamp() {\n r=std::fmax(0,std::fmin(255,r)); g=std::fmax(0,std::fmin(255,g));\n b=std::fmax(0,std::fmin(255,b)); a=std::fmax(0,std::fmin(255,a));\n return *this;\n }\n PColor multRGB(float s) const { return PColor(r*s, g*s, b*s, a); }\n\n // Blend modes (return new color)\n static PColor blend(const PColor& src, const PColor& dst) {\n float sa = src.a/255.f;\n return PColor(src.r*sa+dst.r*(1-sa), src.g*sa+dst.g*(1-sa), src.b*sa+dst.b*(1-sa), 255);\n }\n static PColor add(const PColor& a, const PColor& b) {\n return PColor(std::fmin(255,a.r+b.r), std::fmin(255,a.g+b.g), std::fmin(255,a.b+b.b), a.a);\n }\n static PColor multiply(const PColor& a, const PColor& b) {\n return PColor((a.r/255.f)*b.r, (a.g/255.f)*b.g, (a.b/255.f)*b.b, a.a);\n }\n static PColor screen(const PColor& a, const PColor& b) {\n auto sc=[](float x,float y){ return 255-(255-x)*(255-y)/255.f; };\n return PColor(sc(a.r,b.r), sc(a.g,b.g), sc(a.b,b.b), a.a);\n }\n\n float brightness255() const { return std::fmax(r, std::fmax(g, b)); }\n\n std::string toString() const {\n std::ostringstream ss;\n ss << "PColor(" << r << ", " << g << ", " << b << ", " << a << ")";\n return ss.str();\n }\n};\n\n// Forward declarations so PColor overloads compile below class definitions\nvoid fill(const PColor& c);\nvoid stroke(const PColor& c);\nvoid background(const PColor& c);\nvoid tint(const PColor& c);\n\n// IMAGE FILTER CONSTANTS\n// =============================================================================\n\nstatic constexpr int GRAY = 1;\nstatic constexpr int INVERT = 2;\nstatic constexpr int THRESHOLD = 3;\nstatic constexpr int BLUR_FILTER = 4;\nstatic constexpr int POSTERIZE = 5;\n\n// =============================================================================\n// PIMAGE -- Pixel buffer backed by an OpenGL texture\n// =============================================================================\n\nclass PImage {\npublic:\n int width = 0;\n int height = 0;\n std::vector<unsigned int> pixels;\n GLuint texID = 0;\n bool dirty = false;\n\n PImage() = default;\n PImage(int w, int h) {\n // Guard against bad dimensions from corrupted files or failed loads\n if (w > 0 && h > 0 && w < 16384 && h < 16384) {\n width = w; height = h;\n pixels.assign((size_t)w * h, 0xFF000000);\n }\n }\n\n // Pixel read/write (bounds-checked)\n unsigned int get(int x, int y) const {\n if (x<0||x>=width||y<0||y>=height) return 0;\n return pixels[y*width+x];\n }\n void set(int x, int y, unsigned int c) {\n if (x<0||x>=width||y<0||y>=height) return;\n pixels[y*width+x] = c;\n dirty = true;\n }\n\n // These mirror the Processing Java API; dirty flag is used by updatePixels()\n void loadPixels() {}\n void updatePixels() { dirty = true; }\n\n // Upload CPU pixels to the GPU texture\n void uploadTexture(); // defined in Processing.cpp\n\n void resize(int w, int h) { width=w; height=h; pixels.assign(w*h, 0xFF000000); dirty=true; }\n\n // Apply an image filter to all pixels\n void filter(int mode) {\n for (auto& p : pixels) {\n int r=(p>>16)&0xFF, g=(p>>8)&0xFF, b=p&0xFF, a=(p>>24)&0xFF;\n if (mode == GRAY) { int gr=(r+g+b)/3; p=(a<<24)|(gr<<16)|(gr<<8)|gr; }\n else if (mode == INVERT) { p=(a<<24)|((255-r)<<16)|((255-g)<<8)|(255-b); }\n else if (mode == THRESHOLD) { int gr=(r+g+b)/3; int t=gr>127?255:0; p=(a<<24)|(t<<16)|(t<<8)|t; }\n }\n dirty = true;\n }\n\n // Extract a sub-image\n PImage get(int x, int y, int w, int h) const {\n PImage out(w, h);\n for (int iy=0; iy<h; iy++)\n for (int ix=0; ix<w; ix++)\n out.pixels[iy*w+ix] = get(x+ix, y+iy);\n return out;\n }\n\n // Copy from another image with scaling\n void copy(const PImage& src, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh) {\n for (int iy=0; iy<dh; iy++)\n for (int ix=0; ix<dw; ix++) {\n int srcX = sx+(int)(ix*(float)sw/dw);\n int srcY = sy+(int)(iy*(float)sh/dh);\n set(dx+ix, dy+iy, src.get(srcX, srcY));\n }\n dirty = true;\n }\n\n // Apply alpha mask from another grayscale image\n void mask(const PImage& m) {\n for (int i=0; i<width*height && i<(int)m.pixels.size(); i++) {\n int a = (m.pixels[i]>>16)&0xFF;\n pixels[i] = (pixels[i]&0x00FFFFFF)|(a<<24);\n }\n dirty = true;\n }\n void mask(const PImage* m) { if (m) mask(*m); }\n\n // Destructor frees GPU texture\n virtual ~PImage() { if (texID) glDeleteTextures(1, &texID); }\n\n // Non-copyable (owns GPU resource -- use PImage* for assignment)\n PImage(const PImage&) __attribute__((error(\n "E0002: PImage value-style copying is not supported. "\n "Declare PImage* instead of PImage. "\n "See " PROCESSING_WEBSITE_URL "/error/E0002.html"\n )));\n PImage& operator=(const PImage&) __attribute__((error(\n "E0002: PImage value-style assignment is not supported. "\n "Declare PImage* instead of PImage. "\n "See " PROCESSING_WEBSITE_URL "/error/E0002.html"\n )));\n\n // Movable\n PImage(PImage&& o) noexcept\n : width(o.width), height(o.height), pixels(std::move(o.pixels)),\n texID(o.texID), dirty(o.dirty) { o.texID=0; }\n};\n\n// =============================================================================\n// PGRAPHICS -- Off-screen render target (framebuffer object)\n// =============================================================================\n\nclass PGraphics : public PImage {\npublic:\n GLuint fbo = 0; // framebuffer object\n GLuint rbo = 0; // renderbuffer (depth+stencil)\n bool active = false;\n\n // Independent per-buffer style state. Real Processing\'s PGraphics has\n // its OWN fill/stroke/text/etc. settings, completely separate from the\n // main canvas\'s -- setting fill() on the main canvas must never affect\n // a PGraphics buffer, and vice versa. Previously every PGraphics method\n // forwarded directly to the single global PApplet::g_papplet singleton,\n // meaning style state silently bled between the main canvas and every\n // buffer (e.g. a thick green main-canvas stroke would incorrectly show\n // up on an ellipse drawn inside a buffer that never set its own\n // stroke). beginDraw()/endDraw() now swap PApplet\'s current style out\n // for this buffer\'s OWN remembered style, and swap it back after,\n // exactly mirroring how Java\'s PGraphics keeps independent state.\n struct StyleSnapshot {\n // Defaults match PApplet\'s own real defaults (white fill, black\n // stroke) -- these are also real Processing\'s documented\n // beginDraw() defaults ("Sets the default properties"), NOT an\n // arbitrary choice. The earlier version of this struct had\n // fillR=0 (black fill), which is backwards -- every fresh\n // PGraphics buffer with no explicit fill()/stroke() calls should\n // look exactly like a freshly created Processing sketch: white\n // fill, black stroke, weight 1.\n float fillR=1, fillG=1, fillB=1, fillA=1;\n float strokeR=0, strokeG=0, strokeB=0, strokeA=1;\n float strokeW=1;\n bool doFill=true, doStroke=true, smoothing=true;\n // BUG FIX: these were raw 0/0/0 literals, which silently meant\n // CORNER mode (CORNER=0) for ellipseMode specifically, when real\n // Processing\'s actual default is CENTER (=3). That made every\n // fresh PGraphics buffer\'s ellipse() calls interpret their first\n // two arguments as the bounding box\'s top-left corner instead of\n // its center, shifting every default-mode ellipse by half its\n // width/height toward the bottom-right. rectMode\'s and\n // imageMode\'s real defaults ARE actually CORNER (=0), so those\n // two were correct by coincidence -- only currentEllipseMode\n // needed the real CENTER constant.\n // Using literal values, not the CORNER/CENTER named constants:\n // those constants are declared later in this file, after\n // PGraphics\'s own definition, so they\'re not in scope yet here.\n // CORNER=0, CENTER=3 (see the static constexpr declarations\n // further down in this file).\n int currentRectMode=0 /*CORNER*/, currentEllipseMode=3 /*CENTER*/, currentImageMode=0 /*CORNER*/;\n float tintR=1, tintG=1, tintB=1, tintA=1;\n bool doTint=false;\n int colorModeVal=0;\n float colorMaxH=255.f, colorMaxS=255.f, colorMaxB=255.f, colorMaxA=255.f;\n float g_textSize=14.0f;\n int g_textAlignX=0, g_textAlignY=0;\n float g_textLeading=0.0f;\n bool initialized=false; // false until beginDraw() runs once and sets real Processing defaults\n };\n StyleSnapshot myStyle; // this buffer\'s OWN persistent style\n StyleSnapshot _savedMainStyle; // main canvas\'s style, stashed during beginDraw()..endDraw()\n\n // Multisampled render target: PGraphics now matches real Processing\'s\n // default antialiasing (smooth(2) on P2D/P3D) by rendering into a\n // multisample renderbuffer-backed FBO, then resolving (blitting) down\n // into the plain texture-backed FBO that drawPGraphicsRect samples\n // from. Without this, the main canvas\'s window-level MSAA never\n // applied to off-screen buffers at all.\n GLuint msaaFbo = 0;\n GLuint msaaColorRbo = 0;\n GLuint msaaDepthRbo = 0;\n int samples = 0; // 0 = no multisampling\n bool is3D = false; // true if created via createGraphics(w,h,P3D)\n\n PGraphics() = default;\n\n PGraphics(int w, int h) : PImage(w, h) {\n // Can\'t reach PApplet::g_papplet here -- PApplet\'s complete type\n // isn\'t available yet at this point in the header (PGraphics is\n // defined before it). Default directly to real Processing\'s own\n // P2D/P3D default (smooth(2)) rather than reaching across that\n // forward-reference gap. A sketch wanting a different level for\n // its buffers can extend this later if needed.\n samples = 0; // TEMPORARY: forced off to test if MSAA itself is the bug\n\n // Resolve target: plain, non-multisampled FBO + texture --\n // unchanged from before, just filled via a blit-resolve now.\n glGenFramebuffers(1, &fbo);\n glBindFramebuffer(GL_FRAMEBUFFER, fbo);\n\n if (texID == 0) glGenTextures(1, &texID);\n glBindTexture(GL_TEXTURE_2D, texID);\n glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);\n glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);\n glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\n glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texID, 0);\n\n glGenRenderbuffers(1, &rbo);\n glBindRenderbuffer(GL_RENDERBUFFER, rbo);\n glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, w, h);\n glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);\n\n glBindFramebuffer(GL_FRAMEBUFFER, 0);\n\n // Multisample render target: only created if antialiasing was\n // actually requested. beginDraw() binds THIS one; endDraw() blits\n // it down into the resolve target above.\n if (samples > 0) {\n glGenFramebuffers(1, &msaaFbo);\n glBindFramebuffer(GL_FRAMEBUFFER, msaaFbo);\n glGenRenderbuffers(1, &msaaColorRbo);\n glBindRenderbuffer(GL_RENDERBUFFER, msaaColorRbo);\n glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8, w, h);\n glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaColorRbo);\n glGenRenderbuffers(1, &msaaDepthRbo);\n glBindRenderbuffer(GL_RENDERBUFFER, msaaDepthRbo);\n glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH24_STENCIL8, w, h);\n glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, msaaDepthRbo);\n GLenum msaaStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);\n if (msaaStatus != GL_FRAMEBUFFER_COMPLETE) {\n glDeleteFramebuffers(1, &msaaFbo); msaaFbo = 0;\n glDeleteRenderbuffers(1, &msaaColorRbo); msaaColorRbo = 0;\n glDeleteRenderbuffers(1, &msaaDepthRbo); msaaDepthRbo = 0;\n samples = 0;\n }\n glBindFramebuffer(GL_FRAMEBUFFER, 0);\n }\n }\n\n PGraphics(int w, int h, bool threeD) : PGraphics(w, h) {\n is3D = threeD;\n }\n\n GLint savedViewport[4] = {};\n void beginDraw(); // defined after PApplet (needs its complete type for style swap)\n void _endDrawImpl(); // body of endDraw(), out-of-line for the same reason\n void endDraw() { _endDrawImpl(); }\n\n // Drawing methods forwarded to Processing -- implemented after full decls\n void background(float g); void background(float r, float g, float b); void background(float r, float g, float b, float a);\n void fill(float g); void fill(float r, float g, float b); void fill(float r, float g, float b, float a);\n void noFill(); void stroke(float g); void stroke(float r, float g, float b); void noStroke();\n void strokeWeight(float w);\n void ellipse(float x, float y, float w, float h);\n void rect(float x, float y, float w, float h);\n void line(float x1, float y1, float x2, float y2);\n void point(float x, float y);\n void triangle(float x1,float y1,float x2,float y2,float x3,float y3);\n void text(const std::string& s, float x, float y);\n void textSize(float size);\n void textAlign(int alignX);\n void textAlign(int alignX, int alignY);\n void translate(float x, float y, float z);\n void rotateX(float angle);\n void rotateY(float angle);\n void rotateZ(float angle);\n void box(float size);\n void box(float w, float h, float d);\n void sphere(float r);\n void lights();\n void noLights();\n void ambientLight(float r, float g, float b);\n void ambientLight(float r, float g, float b, float x, float y, float z);\n void directionalLight(float r, float g, float b, float nx, float ny, float nz);\n void pointLight(float r, float g, float b, float x, float y, float z);\n void spotLight(float r, float g, float b, float x, float y, float z,\n float nx, float ny, float nz, float angle, float conc);\n void lightFalloff(float c, float l, float q);\n void lightSpecular(float r, float g, float b);\n void translate(float x, float y); void rotate(float a); void scale(float s);\n void pushMatrix(); void popMatrix();\n void beginShape(); void endShape(int mode=0); void vertex(float x, float y);\n void clear();\n\n ~PGraphics() {\n // Defensive cleanup: a PGraphics can be destroyed (via delete, or\n // by going out of scope) while its beginDraw() was never matched\n // with an endDraw() -- e.g. "pg = createGraphics(...)" reassigns\n // a pointer, leaking the OLD PGraphics it pointed to if nothing\n // explicitly deleted it first; if something DOES eventually\n // delete it (or CppBuild auto-inserts a delete for exactly this\n // case), the destructor running mid-beginDraw() needs to\n // gracefully unwind that state rather than leaving the matrix\n // stack unbalanced or GL bindings dangling on whatever context\n // outlives this object.\n if (active) {\n PDEBUG("PGraphics::~PGraphics: destroying while still active "\n "(beginDraw() never matched with endDraw()) -- "\n "auto-closing now. this=%p\\n", (void*)this);\n _endDrawImpl();\n }\n if (msaaFbo) glDeleteFramebuffers(1, &msaaFbo);\n if (msaaColorRbo) glDeleteRenderbuffers(1, &msaaColorRbo);\n if (msaaDepthRbo) glDeleteRenderbuffers(1, &msaaDepthRbo);\n if (fbo) glDeleteFramebuffers(1, &fbo);\n if (rbo) glDeleteRenderbuffers(1, &rbo);\n }\n\n PGraphics(const PGraphics&) __attribute__((error(\n "E0001: PGraphics value-style copying is not supported. "\n "Declare PGraphics* instead of PGraphics. "\n "See " PROCESSING_WEBSITE_URL "/error/E0001.html"\n )));\n PGraphics& operator=(const PGraphics&) __attribute__((error(\n "E0001: PGraphics value-style assignment is not supported. "\n "Declare PGraphics* instead of PGraphics. "\n "See " PROCESSING_WEBSITE_URL "/error/E0001.html"\n )));\n // Allow assignment from pointer (PGraphics pg; pg = createGraphics(w,h))\n // [E0001] REMOVED: the legacy "PGraphics pg; pg = createGraphics(...);"\n // value-style assignment is no longer supported. PGraphics owns\n // unique GPU resources (FBO, renderbuffers, texture) -- unlike\n // PShape/PFont, which hold only plain CPU-side data and are safely\n // copyable, copying or reassigning a PGraphics VALUE has no safe\n // meaning. Declare it as a pointer instead:\n //\n // PGraphics* pg;\n // pg = createGraphics(w, h);\n // pg->beginDraw();\n // ...\n // pg->endDraw();\n //\n // This explicit compile error is intentional: it tells you exactly\n // what to fix, rather than silently compiling against a value-style\n // declaration that would behave incorrectly or unsafely. The actual\n // URL in the error message below comes from PROCESSING_WEBSITE_URL\n // (ultimately config/cppmode.properties), never hardcoded here.\n PGraphics& operator=(PGraphics* p) __attribute__((error(\n "E0001: PGraphics value-style assignment is not supported. "\n "Declare PGraphics* instead of PGraphics. "\n "See " PROCESSING_WEBSITE_URL "/error/E0001"\n )));\n};\n\n// =============================================================================\n// =============================================================================\n\n\n// Aliases: \'width\' and \'height\' are the canonical Processing names\n// width/height always equal what size() set -- never corrupted by WM tile resize.\n\n// =============================================================================\n// =============================================================================\n\n\n// =============================================================================\n// =============================================================================\n// Define any of these in your sketch; unimplemented ones are safely skipped.\n//\n// On Linux/macOS: declared __attribute__((weak)) so undefined ones link as nullptr.\n// On Windows (MinGW): weak declarations don\'t work; instead, _wireCallbacksFn is\n// set at the bottom of IDE.cpp/sketch to point to a function that wires\n// all _on* function pointers. See the Windows Event Wiring section of IDE.cpp.\n\n// ---------------------------------------------------------------------------\n// Processing event callbacks -- define whichever ones your sketch needs.\n// Processing.cpp uses _on* function pointers; wireCallbacks() in\n// Sketch_run.cpp assigns only the ones the sketch defines.\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Internal event function pointers (set by run() via the callbacks above).\n// Exposed here so IDE.cpp\'s wireCallbacks() can assign them.\n// ---------------------------------------------------------------------------\n#include <functional>\n\n// ---------------------------------------------------------------------------\n// Windows-only: raw POD function pointer set by IDE.cpp during static init.\n// POD is guaranteed zero-initialized before any constructor runs, so writing\n// to it from a static initializer in another translation unit is always safe.\n// Processing::run() calls it (if non-null) after setup() to wire all _on* ptrs.\n// ---------------------------------------------------------------------------\n\n// =============================================================================\n// =============================================================================\n\n\n// =============================================================================\n// =============================================================================\n\n\n// =============================================================================\n// =============================================================================\n\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\n// Mouse buttons\n// mouseButton is set to LEFT(37), RIGHT(39), or CENTER(3) when a button is pressed\n\n// ---------------------------------------------------------------------------\n// Processing reference constants\n// Key codes match Java KeyEvent.VK_* values exactly.\n// Mouse button constants match Processing\'s LEFT/CENTER/RIGHT.\n// ---------------------------------------------------------------------------\n\n// key == CODED when a non-ASCII special key is pressed; then check keyCode\nstatic constexpr int CODED = 0xFFFF; // matches real Processing\'s PConstants.CODED exactly (was incorrectly 0xFF, off by a factor of 256)\n\n// Coded keys (keyCode values, Java KeyEvent.VK_*)\nstatic constexpr int UP = 38;\nstatic constexpr int DOWN = 40;\nstatic constexpr int LEFT = 37; // arrow key AND left mouse button\nstatic constexpr int RIGHT = 39; // arrow key AND right mouse button\nstatic constexpr int ALT = 18;\nstatic constexpr int CONTROL = 17;\nstatic constexpr int SHIFT = 16;\nstatic constexpr int HOME_KEY = 36;\nstatic constexpr int END_KEY = 35;\nstatic constexpr int PAGE_UP = 33;\nstatic constexpr int PAGE_DOWN = 34;\nstatic constexpr int F1_KEY = 112; static constexpr int F2_KEY = 113;\nstatic constexpr int F3_KEY = 114; static constexpr int F4_KEY = 115;\nstatic constexpr int F5_KEY = 116; static constexpr int F6_KEY = 117;\nstatic constexpr int F7_KEY = 118; static constexpr int F8_KEY = 119;\nstatic constexpr int F9_KEY = 120; static constexpr int F10_KEY = 121;\nstatic constexpr int F11_KEY = 122; static constexpr int F12_KEY = 123;\n\n// Non-coded keys: use `key` directly (not keyCode) for these\nstatic constexpr char BACKSPACE = 8;\nstatic constexpr char TAB = 9;\nstatic constexpr char ENTER = 10; // PC/Unix enter key\n// Undefine any system macros that might conflict\n#ifdef RETURN\n# undef RETURN\n#endif\n#ifdef DELETE\n# undef DELETE\n#endif\nstatic constexpr int RETURN = 13; // Mac return key (same key as ENTER on most systems)\nstatic constexpr int ESC = 27;\nstatic constexpr int DELETE = 127;\n\n// Mouse buttons (mouseButton variable, Java MouseEvent values)\nstatic constexpr int CENTER = 3; // middle mouse button; also rectMode/ellipseMode CENTER\n\n// Color modes\nstatic constexpr int RGB = 1;\nstatic constexpr int HSB = 2;\n#define ARGB 3 /* createImage(w,h,ARGB) */\n\n// Shape / rect / ellipse modes\nstatic constexpr int CORNER = 0;\nstatic constexpr int CORNERS = 1;\nstatic constexpr int RADIUS = 2;\n\n// Stroke caps and joins\nstatic constexpr int ROUND = 10;\nstatic constexpr int SQUARE = 11;\nstatic constexpr int PROJECT = 12;\nstatic constexpr int MITER = 13;\nstatic constexpr int BEVEL = 14;\n\n// beginShape() kinds\nstatic constexpr int POINTS = 0;\nstatic constexpr int LINES = 1;\nstatic constexpr int TRIANGLES = 2;\nstatic constexpr int TRIANGLE_FAN = 3;\nstatic constexpr int TRIANGLE_STRIP = 4;\nstatic constexpr int QUADS = 5;\nstatic constexpr int QUAD_STRIP = 6;\nstatic constexpr int CLOSE = 7;\n\n// Text alignment\nstatic constexpr int LEFT_ALIGN = 20;\nstatic constexpr int RIGHT_ALIGN = 21;\nstatic constexpr int TOP_ALIGN = 22;\nstatic constexpr int BOTTOM_ALIGN = 23;\nstatic constexpr int BASELINE = 24;\nstatic constexpr int CENTER_ALIGN = 25;\n\n// Blend modes\nstatic constexpr int BLEND = 30;\nstatic constexpr int ADD = 31;\nstatic constexpr int SUBTRACT = 32;\nstatic constexpr int MULTIPLY = 33;\nstatic constexpr int SCREEN = 34;\nstatic constexpr int DARKEST = 35;\nstatic constexpr int LIGHTEST = 36;\nstatic constexpr int DIFFERENCE = 37;\nstatic constexpr int EXCLUSION = 38;\n\n// Math constants (float precision)\nstatic constexpr float PI = static_cast<float>(M_PI);\nstatic constexpr float TWO_PI = static_cast<float>(M_PI * 2.0);\nstatic constexpr float HALF_PI = static_cast<float>(M_PI / 2.0);\nstatic constexpr float QUARTER_PI = static_cast<float>(M_PI / 4.0);\nstatic constexpr float TAU = TWO_PI; // alias\n\n// Renderer flags for size()\nstatic constexpr int P2D = 2;\nstatic constexpr int P3D = 3;\n\n// Texture / image modes\nstatic constexpr int IMAGE = 100;\nstatic constexpr int NORMAL = 101;\nstatic constexpr int CLAMP = 102;\nstatic constexpr int REPEAT = 103;\n\n// hint() flags\nstatic constexpr int ENABLE_DEPTH_TEST = 1;\nstatic constexpr int DISABLE_DEPTH_TEST = -1;\nstatic constexpr int ENABLE_DEPTH_SORT = 2;\nstatic constexpr int DISABLE_DEPTH_SORT = -2;\nstatic constexpr int ENABLE_OPENGL_ERRORS = 3;\nstatic constexpr int DISABLE_OPENGL_ERRORS = -3;\nstatic constexpr int ENABLE_STROKE_PERSPECTIVE = 4;\nstatic constexpr int DISABLE_STROKE_PERSPECTIVE= -4;\nstatic constexpr int ENABLE_TEXTURE_MIPMAPS = 5;\nstatic constexpr int DISABLE_TEXTURE_MIPMAPS = -5;\n\n// Cursor shapes (map to GLFW)\nstatic constexpr int ARROW = GLFW_ARROW_CURSOR;\nstatic constexpr int CROSS = GLFW_CROSSHAIR_CURSOR;\nstatic constexpr int HAND = GLFW_HAND_CURSOR; // GLFW_POINTING_HAND_CURSOR in 3.4+\nstatic constexpr int MOVE = GLFW_HRESIZE_CURSOR; // GLFW_RESIZE_ALL_CURSOR in 3.4+\nstatic constexpr int TEXT_CURSOR = GLFW_IBEAM_CURSOR;\nstatic constexpr int WAIT = GLFW_VRESIZE_CURSOR; // GLFW_RESIZE_ALL_CURSOR in 3.4+\n\n// =============================================================================\n// TIMING -- inline so they compile anywhere without linking Processing.cpp\n// =============================================================================\n\ninline unsigned long millis() {\n using namespace std::chrono;\n static auto start = steady_clock::now();\n return static_cast<unsigned long>(duration_cast<milliseconds>(steady_clock::now()-start).count());\n}\ninline int second() { std::time_t t=std::time(nullptr); return std::localtime(&t)->tm_sec; }\ninline int minute() { std::time_t t=std::time(nullptr); return std::localtime(&t)->tm_min; }\ninline int hour() { std::time_t t=std::time(nullptr); return std::localtime(&t)->tm_hour; }\ninline int day() { std::time_t t=std::time(nullptr); return std::localtime(&t)->tm_mday; }\ninline int month() { std::time_t t=std::time(nullptr); return std::localtime(&t)->tm_mon+1; }\ninline int year() { std::time_t t=std::time(nullptr); return std::localtime(&t)->tm_year+1900;}\n\n// \'color\' is a packed 32-bit ARGB integer, just like in Processing Java.\n// Constructors respect the current colorMode setting (see colorMode()).\nstruct color {\n unsigned int value;\n\n color() : value(0xFF000000) {} // default: opaque black\n color(unsigned int v) : value(v) {} // allows color pix = img->get(x,y)\n\n // Defined in Processing.cpp so the colorMode globals are accessible.\n //\n // 3-arg and 4-arg int overloads were intentionally removed: they were\n // pure pass-throughs that cast to float and called the exact same\n // _makeColor() the float overloads call directly, so removing them\n // changes no runtime behavior -- it only removes the possibility of\n // ambiguous overload resolution on mixed-type calls like\n // color(map(...), map(...), 50).\n //\n // 1-arg and 2-arg int overloads are KEPT: color(int) would otherwise be\n // ambiguous between color(float) (grayscale) and color(unsigned int)\n // (raw packed ARGB pixel value, e.g. color pix = img->get(x,y)) -- two\n // different, equally-valid implicit conversions for the same int\n // literal. color(int gray) as an EXACT match resolves that in favor\n // of "grayscale", matching real Processing semantics.\n color(int gray);\n color(int gray, int a);\n // int 3/4-arg overloads: these were previously removed to avoid overload\n // resolution ambiguity, but are restored because:\n // (a) int->float promotion makes int args exact-match int overloads rather\n // than ambiguously matching float ones, so no ambiguity occurs in practice\n // (b) without them, "color c(r, g, b)" with int r,g,b stays as paren-init\n // (color is in INITIALIZER_LIST_AMBIGUOUS_TYPES) but then fails to compile\n // since color(float,float,float) requires narrowing conversion of int->float\n // in direct-init context.\n color(int r, int g, int b);\n color(int r, int g, int b, int a);\n // Use explicit cast: color(0,153,204,(int)a) or color(0.f,153.f,204.f,a)\n color(float gray);\n color(float gray, float a);\n color(float r, float g, float b);\n color(float r, float g, float b, float a);\n\n explicit operator unsigned int() const { return value; }\n unsigned int toInt() const { return value; }\n // In Processing Java, color IS int. Allow implicit color<->int conversion\n // so sketches can write: int c = color(255); int c = lerpColor(a,b,t);\n operator int() const { return (int)value; }\n // fromRaw: convert raw int (Java color int) directly to color value\n static color fromRaw(int v) { color c; c.value=(unsigned int)v; return c; }\n bool operator==(const color& o) const { return value == o.value; }\n bool operator!=(const color& o) const { return value != o.value; }\n};\n\n// Build a color value from components (respects colorMode)\n\n// Pack raw 0-255 RGBA without colorMode (for internal use)\ninline color colorVal(int r, int g, int b, int a=255) {\n // Clamp to [0,255] -- don\'t wrap, which would cause dark artifacts\n // when noise()*255 or other values slightly exceed 255\n auto clamp8=[](int v){return v<0?0:v>255?255:v;};\n return color((unsigned int)(((clamp8(a))<<24)|((clamp8(r))<<16)|((clamp8(g))<<8)|(clamp8(b))));\n}\n\n// Color component extractors\n\n// =============================================================================\n// PRINT / OUTPUT\n// =============================================================================\n\ntemplate<typename T> inline void print(const T& v) { std::cout << v; std::cout.flush(); }\ntemplate<typename T> inline void println(const T& v) { std::cout << v << "\\n"; std::cout.flush(); }\ninline void println() { std::cout << "\\n"; std::cout.flush(); }\ntemplate<typename T> inline void printArray(const std::vector<T>& a) {\n for (size_t i=0; i<a.size(); i++) std::cout << "[" << i << "] " << a[i] << "\\n";\n}\n\n// =============================================================================\n// STRING UTILITIES\n// =============================================================================\n\ninline std::string str(int v) { return std::to_string(v); }\ninline std::string str(float v) { return std::to_string(v); }\ninline std::string str(bool v) { return v ? "true" : "false"; }\ninline std::string str(char v) { return std::string(1, v); }\n// char16_t overload -- matches Java\'s actual "char" type for key/keyTyped\n// etc., which is 16-bit (UTF-16). For values within the basic ASCII/\n// Latin-1 range (which covers everything our engine\'s keyboard handling\n// actually produces), this prints the single character exactly like\n// str(char) does. CODED (0xFFFF) itself isn\'t really meant to be\n// printed as text at all (matching real Processing -- see PConstants.\n// CODED\'s own doc comment, "key will be CODED"), so this just produces\n// SOME single-character output for it rather than special-casing it.\ninline std::string str(char16_t v) { return std::string(1, (char)v); }\ninline bool toBoolean(const std::string& s) { return s=="true"||s=="1"||s=="yes"; }\ninline int toInt(const std::string& s) { return std::stoi(s); }\ninline float toFloat(const std::string& s) { try { return std::stof(s); } catch (...) { return 0.0f; } }\ninline char toChar(int v) { return static_cast<char>(v); }\ninline std::string trim(const std::string& s) {\n size_t a=s.find_first_not_of(" \\t\\n\\r"), b=s.find_last_not_of(" \\t\\n\\r");\n return a==std::string::npos ? "" : s.substr(a, b-a+1);\n}\ninline std::vector<std::string> split(const std::string& s, char d) {\n std::vector<std::string> o; std::stringstream ss(s); std::string t;\n while (std::getline(ss, t, d)) o.push_back(t);\n return o;\n}\ninline std::vector<std::string> splitTokens(const std::string& s, const std::string& delims) {\n std::vector<std::string> o; std::string cur;\n for (char c:s) {\n if (delims.find(c)!=std::string::npos) { if(!cur.empty()){o.push_back(cur);cur.clear();} }\n else cur+=c;\n }\n if (!cur.empty()) o.push_back(cur);\n return o;\n}\ninline std::string join(const std::vector<std::string>& v, const std::string& sep) {\n std::string o;\n for (size_t i=0; i<v.size(); i++) { if(i) o+=sep; o+=v[i]; }\n return o;\n}\n\n// Number formatting\ninline std::string nf(float v, int digits) { std::ostringstream ss; ss.precision(digits); ss<<std::fixed<<v; return ss.str(); }\ninline std::string nf(int v, int minDigits) { std::ostringstream ss; ss<<std::setw(minDigits)<<std::setfill(\'0\')<<v; return ss.str(); }\ninline std::string nf(float v, int left, int right) {\n std::ostringstream ss; ss<<std::fixed<<std::setprecision(right)<<v;\n std::string s=ss.str(); size_t dot=s.find(\'.\');\n size_t intLen=(dot==std::string::npos)?s.size():dot;\n while((int)intLen<left){s="0"+s;intLen++;}\n return s;\n}\ninline std::string nfc(float v, int digits) { std::ostringstream ss; ss.precision(digits); ss<<std::fixed<<v; std::string s=ss.str(); int dot=(int)s.find(\'.\'); if(dot<0)dot=(int)s.size(); for(int i=dot-3;i>0;i-=3)s.insert(i,","); return s; }\ninline std::string nfp(float v, int digits) { return (v>=0?"+":"") + nf(v,digits); }\ninline std::string nfs(float v, int digits) { return (v>=0?" ":"") + nf(v,digits); }\ninline std::string hex(int v) { std::ostringstream ss; ss<<std::uppercase<<std::hex<<v; return ss.str(); }\ninline std::string hex(int v, int digits) { std::ostringstream ss; ss<<std::uppercase<<std::hex<<std::setw(digits)<<std::setfill(\'0\')<<v; return ss.str(); }\ninline std::string binary(int v) { std::string s; for(int i=31;i>=0;i--) s+=((v>>i)&1)?\'1\':\'0\'; return s; }\ninline int unhex(const std::string& s) { return std::stoi(s,nullptr,16); }\ninline int unbinary(const std::string& s){ return std::stoi(s,nullptr,2); }\n\n// Regex helpers\ninline std::vector<std::string> match(const std::string& s, const std::string& pat) {\n std::vector<std::string> out; std::smatch m; std::regex re(pat);\n if (std::regex_search(s,m,re)) for (auto& x:m) out.push_back(x.str());\n return out;\n}\ninline std::vector<std::vector<std::string>> matchAll(const std::string& s, const std::string& pat) {\n std::vector<std::vector<std::string>> out; std::regex re(pat);\n auto it=std::sregex_iterator(s.begin(),s.end(),re), end=std::sregex_iterator();\n for(;it!=end;++it){ std::vector<std::string> row; for(auto& x:*it) row.push_back(x.str()); out.push_back(row); }\n return out;\n}\n\n// =============================================================================\n// FILE I/O\n// =============================================================================\n\ninline std::vector<std::string> loadStrings(const std::string& path) {\n std::vector<std::string> lines; std::ifstream f(path); std::string l;\n while (std::getline(f,l)) lines.push_back(l);\n return lines;\n}\ninline bool saveStrings(const std::string& path, const std::vector<std::string>& lines) {\n std::ofstream f(path); if (!f) return false;\n for (auto& l:lines) f<<l<<"\\n";\n return true;\n}\ninline std::vector<unsigned char> loadBytes(const std::string& path) {\n std::ifstream f(path,std::ios::binary);\n return std::vector<unsigned char>((std::istreambuf_iterator<char>(f)),std::istreambuf_iterator<char>());\n}\ninline bool saveBytes(const std::string& path, const std::vector<unsigned char>& data) {\n std::ofstream f(path,std::ios::binary); if (!f) return false;\n f.write(reinterpret_cast<const char*>(data.data()),data.size());\n return true;\n}\n\n// =============================================================================\n// USER CALLBACKS -- the sketch must define at minimum setup() and draw()\n// =============================================================================\n\n\n// =============================================================================\n// ENVIRONMENT FUNCTIONS\n// =============================================================================\n\n// ---------------------------------------------------------------------------\n// Clipboard, input state, timing, window icon (IDE-facing helpers)\n// ---------------------------------------------------------------------------\n\n\n// Letter key constants -- Java KeyEvent.VK_A=65 .. VK_Z=90\nstatic constexpr int KEY_A=65; static constexpr int KEY_B=66;\nstatic constexpr int KEY_C=67; static constexpr int KEY_D=68;\nstatic constexpr int KEY_E=69; static constexpr int KEY_F=70;\nstatic constexpr int KEY_G=71; static constexpr int KEY_H=72;\nstatic constexpr int KEY_I=73; static constexpr int KEY_J=74;\nstatic constexpr int KEY_K=75; static constexpr int KEY_L=76;\nstatic constexpr int KEY_M=77; static constexpr int KEY_N=78;\nstatic constexpr int KEY_O=79; static constexpr int KEY_P=80;\nstatic constexpr int KEY_Q=81; static constexpr int KEY_R=82;\nstatic constexpr int KEY_S=83; static constexpr int KEY_T=84;\nstatic constexpr int KEY_U=85; static constexpr int KEY_V=86;\nstatic constexpr int KEY_W=87; static constexpr int KEY_X=88;\nstatic constexpr int KEY_Y=89; static constexpr int KEY_Z=90;\n\nstatic constexpr int PERIOD_KEY = 46;\nstatic constexpr int SLASH_KEY = 47;\nstatic constexpr int EQUAL_KEY = 61;\nstatic constexpr int MINUS_KEY = 45;\n\n\n\n// =============================================================================\n// STYLE STACK -- save/restore fill, stroke, transform state\n// =============================================================================\n\n\n// =============================================================================\n// COLOR MODE\n// =============================================================================\n\n// colorMode(RGB) -- all channels [0..255]\n// colorMode(HSB, 360, 100, 100) -- hue [0..360], sat/bri [0..100]\n\n// =============================================================================\n// BACKGROUND / CLEAR\n// =============================================================================\n\n\n\n// =============================================================================\n// FILL / STROKE\n// =============================================================================\n\nstruct PApplet; // forward decl for template bodies\ninline void fill(color c, int a) { fill(c, (float)a); }\n\n\n\n// ── Forward declarations for template helpers ─────────────────────────────────\n// These break infinite recursion in the arithmetic-overload templates below.\n// Implementations are after struct PApplet (which defines g_papplet).\nstruct PApplet;\nnamespace _api {\n void line(float,float,float,float);\n void line(float,float,float,float,float,float);\n void rect(float,float,float,float);\n void rect(float,float,float,float,float);\n void ellipse(float,float,float,float);\n void circle(float,float,float);\n void point(float,float);\n void point(float,float,float);\n void triangle(float,float,float,float,float,float);\n void quad(float,float,float,float,float,float,float,float);\n void arc(float,float,float,float,float,float);\n void arc(float,float,float,float,float,float,int);\n void translate(float,float);\n void translate(float,float,float);\n void scale(float,float);\n void vertex(float,float);\n void vertex(float,float,float);\n void vertex(float,float,float,float);\n void bezier(float,float,float,float,float,float,float,float);\n void curve(float,float,float,float,float,float,float,float);\n void text(float,float,float);\n void text(const std::string&,float,float);\n void text(const std::string&,float,float,float,float);\n float map(float,float,float,float,float);\n float constrain(float,float,float);\n float lerp(float,float,float);\n void fill(float);\n void fill(float,float);\n void fill(float,float,float);\n void fill(float,float,float,float);\n void stroke(float);\n void stroke(float,float);\n void stroke(float,float,float);\n void stroke(float,float,float,float);\n void background(float);\n void background(float,float);\n void background(float,float,float);\n void background(float,float,float,float);\n void tint(float);\n void tint(float,float);\n void tint(float,float,float);\n void tint(float,float,float,float);\n void strokeWeight(float);\n void rotate(float);\n}\n\n// int overloads\n// ---------------------------------------------------------------------------\n// Mixed-type overloads for fill, stroke, background, tint.\n// These templates accept any arithmetic type (int, float, double, etc.)\n// and forward to the canonical float versions, eliminating all ambiguity\n// from mixed calls like fill(int, int, float) or stroke(float, int, float).\n// ---------------------------------------------------------------------------\ntemplate<typename A, typename B,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\ninline void fill(A gray, B a)\n { _api::fill((float)gray,(float)a); }\n\ntemplate<typename A, typename B, typename C,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\ninline void fill(A r, B g, C b)\n { _api::fill((float)r,(float)g,(float)b); }\n\ntemplate<typename A, typename B, typename C, typename D,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\ninline void fill(A r, B g, C b, D a)\n { _api::fill((float)r,(float)g,(float)b,(float)a); }\n\ntemplate<typename A,\n typename=std::enable_if_t<std::is_arithmetic_v<A>>>\ninline void stroke(A gray)\n { _api::stroke((float)gray); }\n\ntemplate<typename A, typename B,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\ninline void stroke(A gray, B a)\n { _api::stroke((float)gray,(float)a); }\n\ntemplate<typename A, typename B, typename C,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\ninline void stroke(A r, B g, C b)\n { _api::stroke((float)r,(float)g,(float)b); }\n\ntemplate<typename A, typename B, typename C, typename D,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\ninline void stroke(A r, B g, C b, D a)\n { _api::stroke((float)r,(float)g,(float)b,(float)a); }\n\ntemplate<typename A,\n typename=std::enable_if_t<std::is_arithmetic_v<A>>>\ninline void strokeWeight(A w)\n { _api::strokeWeight((float)w); }\n\ntemplate<typename A,\n typename=std::enable_if_t<std::is_arithmetic_v<A>>>\ninline void fill(A gray)\n { _api::fill((float)gray); }\n\ntemplate<typename A,\n typename=std::enable_if_t<std::is_arithmetic_v<A>>>\ninline void background(A gray)\n { _api::background((float)gray); }\n\ntemplate<typename A, typename B,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\ninline void background(A gray, B a)\n { _api::background((float)gray,(float)a); }\n\ntemplate<typename A, typename B, typename C,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\ninline void background(A r, B g, C b)\n { _api::background((float)r,(float)g,(float)b); }\n\ntemplate<typename A, typename B, typename C, typename D,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\ninline void background(A r, B g, C b, D a)\n { _api::background((float)r,(float)g,(float)b,(float)a); }\n\n\n// Integer-only templates: these cast int args to float and call the float overloads.\n// Constrained to non-float types so float calls go directly to the float overload\n// above and don\'t recurse back into the template.\ntemplate<typename A, typename=std::enable_if_t<std::is_arithmetic_v<A>>>\ninline void tint(A gray)\n { _api::tint((float)gray); }\n\ntemplate<typename A, typename B,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\ninline void tint(A gray, B a)\n { _api::tint((float)gray,(float)a); }\n\ntemplate<typename A, typename B, typename C,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\ninline void tint(A r, B g, C b)\n { _api::tint((float)r,(float)g,(float)b); }\n\ntemplate<typename A, typename B, typename C, typename D,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\ninline void tint(A r, B g, C b, D a)\n { _api::tint((float)r,(float)g,(float)b,(float)a); }\n\n// =============================================================================\n// SHAPE ATTRIBUTES\n// =============================================================================\n\n\n// =============================================================================\n// 2D PRIMITIVES\n// =============================================================================\n\n\n// Mixed-type templates are in the comprehensive block at end of namespace\n\n// =============================================================================\n// 3D PRIMITIVES\n// =============================================================================\n\n\n// =============================================================================\n// CUSTOM SHAPES -- beginShape / vertex / endShape\n// =============================================================================\n\n\n// =============================================================================\n// MATRIX TRANSFORMS\n// =============================================================================\n\n\n// =============================================================================\n// CAMERA -- 3D view projection\n// =============================================================================\n\n\n// =============================================================================\n// LIGHTS\n// =============================================================================\n\n\n// Material properties\n\n// =============================================================================\n// TEXT\n// =============================================================================\n\n\n// =============================================================================\n// IMAGE FUNCTIONS\n// =============================================================================\n\n\n\nPImage getRegion(int x, int y, int w, int h);\n\n// =============================================================================\n// BLEND / CLIP\n// =============================================================================\n\n\n// =============================================================================\n// SAVE / THREADING\n// =============================================================================\n\ninline void thread(std::function<void()> fn) { std::thread(fn).detach(); }\ninline void delay(int ms) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); }\n\n// =============================================================================\n// ENTRY POINT\n// =============================================================================\n\n\n// Opens a console window for stderr output on Windows when --debug flag is passed.\n// Call this before run() -- see main.cpp.\n\n// =============================================================================\n// JSON\n// =============================================================================\n\nstruct JSONValue;\nusing JSONObject = std::map<std::string, JSONValue>;\nusing JSONArray = std::vector<JSONValue>;\n\nstruct JSONValue {\n enum Type { NULL_T,BOOL_T,INT_T,FLOAT_T,STRING_T,ARRAY_T,OBJECT_T } type=NULL_T;\n bool b=false; double n=0; std::string s;\n std::shared_ptr<JSONArray> arr;\n std::shared_ptr<JSONObject> obj;\n\n JSONValue() = default;\n JSONValue(bool v) : type(BOOL_T), b(v) {}\n JSONValue(int v) : type(INT_T), n(v) {}\n JSONValue(double v) : type(FLOAT_T), n(v) {}\n JSONValue(const std::string& v) : type(STRING_T), s(v) {}\n JSONValue(const char* v) : type(STRING_T), s(v) {}\n JSONValue(JSONArray v) : type(ARRAY_T), arr(std::make_shared<JSONArray>(v)) {}\n JSONValue(JSONObject v) : type(OBJECT_T), obj(std::make_shared<JSONObject>(v)) {}\n\n bool isNull() const { return type==NULL_T; }\n bool isBool() const { return type==BOOL_T; }\n bool isInt() const { return type==INT_T; }\n bool isFloat() const { return type==FLOAT_T || type==INT_T; }\n bool isString() const { return type==STRING_T; }\n bool isArray() const { return type==ARRAY_T; }\n bool isObject() const { return type==OBJECT_T; }\n\n bool getBool() const { return b; }\n int getInt() const { return (int)n; }\n float getFloat() const { return (float)n;}\n std::string getString() const { return s; }\n JSONArray& getArray() { return *arr; }\n JSONObject& getObject() { return *obj; }\n const JSONArray& getArray() const { return *arr; }\n const JSONObject& getObject() const { return *obj; }\n\n JSONValue& operator[](const std::string& k) { return (*obj)[k]; }\n JSONValue& operator[](int i) { return (*arr)[i]; }\n int size() const { if(isArray())return (int)arr->size(); if(isObject())return (int)obj->size(); return 0; }\n bool hasKey(const std::string& k) const { return isObject() && obj->count(k); }\n};\n\n\n// =============================================================================\n// XML\n// =============================================================================\n\nstruct XML {\n std::string name, content;\n std::map<std::string,std::string> attributes;\n std::vector<XML> children;\n\n XML() = default;\n explicit XML(const std::string& n) : name(n) {}\n\n std::string getName() const { return name; }\n std::string getContent() const { return content; }\n\n bool hasAttribute(const std::string& k) const { return attributes.count(k)>0; }\n std::string getAttribute(const std::string& k, const std::string& def="") const {\n auto it = attributes.find(k);\n return it != attributes.end() ? it->second : def;\n }\n int getAttributeInt(const std::string& k, int def=0) const { return hasAttribute(k) ? std::stoi(attributes.at(k)) : def; }\n float getAttributeFloat(const std::string& k, float def=0) const { return hasAttribute(k) ? std::stof(attributes.at(k)) : def; }\n\n void setAttribute(const std::string& k, const std::string& v) { attributes[k]=v; }\n void setContent(const std::string& c) { content=c; }\n\n XML* addChild(const std::string& n) { children.push_back(XML(n)); return &children.back(); }\n XML* getChild(int i) { return i<(int)children.size()?&children[i]:nullptr; }\n XML* getChild(const std::string& n) { for(auto& c:children) if(c.name==n) return &c; return nullptr; }\n int getChildCount() const { return (int)children.size(); }\n std::vector<XML*> getChildren(const std::string& n){ std::vector<XML*> r; for(auto& c:children) if(c.name==n) r.push_back(&c); return r; }\n\n std::string toString(int indent=0) const;\n};\n\n\n// =============================================================================\n// TABLE -- CSV-style data with named columns\n// =============================================================================\n\nclass Table {\npublic:\n std::vector<std::string> columns;\n std::vector<std::vector<std::string>> rows;\n\n Table() = default;\n\n void addColumn(const std::string& name) { columns.push_back(name); }\n int getColumnCount() const { return (int)columns.size(); }\n int getRowCount() const { return (int)rows.size(); }\n std::string getColumnTitle(int i) const { return i<(int)columns.size()?columns[i]:""; }\n int getColumnIndex(const std::string& n) const {\n for (int i=0;i<(int)columns.size();i++) if(columns[i]==n) return i;\n return -1;\n }\n\n std::vector<std::string>& addRow() { rows.push_back(std::vector<std::string>(columns.size())); return rows.back(); }\n\n std::string getString(int row, int col) const { return row<(int)rows.size()&&col<(int)rows[row].size()?rows[row][col]:""; }\n std::string getString(int row, const std::string& col) const { return getString(row,getColumnIndex(col)); }\n int getInt(int row, int col) const { auto s=getString(row,col); return s.empty()?0:std::stoi(s); }\n int getInt(int row, const std::string& col) const { return getInt(row,getColumnIndex(col)); }\n float getFloat(int row, int col) const { auto s=getString(row,col); return s.empty()?0:std::stof(s); }\n float getFloat(int row, const std::string& col) const { return getFloat(row,getColumnIndex(col)); }\n\n void setString(int row, int col, const std::string& v) { if(row<(int)rows.size()&&col<(int)rows[row].size()) rows[row][col]=v; }\n void setString(int row, const std::string& col, const std::string& v) { setString(row,getColumnIndex(col),v); }\n void setInt(int row, int col, int v) { setString(row,col,std::to_string(v)); }\n void setFloat(int row, int col, float v) { setString(row,col,std::to_string(v)); }\n\n std::vector<int> findRowsWithValue(const std::string& col, const std::string& val) const {\n std::vector<int> r; int c=getColumnIndex(col);\n for (int i=0;i<(int)rows.size();i++) if(getString(i,c)==val) r.push_back(i);\n return r;\n }\n int findFirstRowWithValue(const std::string& col, const std::string& val) const {\n auto r=findRowsWithValue(col,val); return r.empty()?-1:r[0];\n }\n void removeRow(int i) { if(i<(int)rows.size()) rows.erase(rows.begin()+i); }\n void clearRows() { rows.clear(); }\n};\n\n\n// =============================================================================\n// TYPED LISTS / DICTS -- match Processing Java\'s IntList, FloatDict, etc.\n// =============================================================================\n\n// =============================================================================\n// String -- real wrapper class with Java\'s String API, NOT a textual\n// rename to std::string. Inherits std::string for storage/operators\n// (+, ==, <<, etc. all keep working), and adds Java-named methods so\n// sketch authors can transfer their Java/Processing knowledge directly:\n// length(), charAt(), equals(), equalsIgnoreCase(), substring(),\n// indexOf(), lastIndexOf(), toLowerCase(), toUpperCase(), trim(),\n// contains(), startsWith(), endsWith(), replace(), isEmpty(), concat(),\n// compareTo(). Regex-based methods (matches/replaceAll/split with regex)\n// are intentionally NOT implemented -- real Processing sketches rarely\n// use them, and a correct regex engine is a much bigger addition.\n// =============================================================================\nclass String : public std::string {\npublic:\n String() : std::string() {}\n String(const std::string& s) : std::string(s) {}\n String(const char* s) : std::string(s) {}\n String(char c) : std::string(1, c) {}\n String(const std::string& s, size_t pos, size_t len = npos) : std::string(s, pos, len) {}\n\n int length() const { return (int)size(); }\n bool isEmpty() const { return empty(); }\n\n char charAt(int index) const { return at((size_t)index); }\n\n bool equals(const std::string& other) const { return *this == other; }\n bool equalsIgnoreCase(const std::string& other) const {\n if (size() != other.size()) return false;\n for (size_t i = 0; i < size(); i++)\n if (tolower((unsigned char)(*this)[i]) != tolower((unsigned char)other[i])) return false;\n return true;\n }\n\n String substring(int beginIndex) const {\n if (beginIndex < 0) beginIndex = 0;\n if ((size_t)beginIndex > size()) beginIndex = (int)size();\n return String(substr((size_t)beginIndex));\n }\n String substring(int beginIndex, int endIndex) const {\n if (beginIndex < 0) beginIndex = 0;\n if (endIndex > (int)size()) endIndex = (int)size();\n if (endIndex < beginIndex) endIndex = beginIndex;\n return String(substr((size_t)beginIndex, (size_t)(endIndex - beginIndex)));\n }\n\n int indexOf(const std::string& needle) const {\n size_t p = find(needle);\n return p == npos ? -1 : (int)p;\n }\n int indexOf(const std::string& needle, int fromIndex) const {\n size_t p = find(needle, (size_t)std::max(0, fromIndex));\n return p == npos ? -1 : (int)p;\n }\n int lastIndexOf(const std::string& needle) const {\n size_t p = rfind(needle);\n return p == npos ? -1 : (int)p;\n }\n\n String toLowerCase() const {\n std::string r = *this;\n for (auto& c : r) c = (char)tolower((unsigned char)c);\n return String(r);\n }\n String toUpperCase() const {\n std::string r = *this;\n for (auto& c : r) c = (char)toupper((unsigned char)c);\n return String(r);\n }\n\n String trim() const {\n size_t start = find_first_not_of(" \\t\\n\\r\\f\\v");\n if (start == npos) return String("");\n size_t end = find_last_not_of(" \\t\\n\\r\\f\\v");\n return String(substr(start, end - start + 1));\n }\n\n bool contains(const std::string& needle) const { return find(needle) != npos; }\n bool startsWith(const std::string& prefix) const {\n return size() >= prefix.size() && compare(0, prefix.size(), prefix) == 0;\n }\n bool endsWith(const std::string& suffix) const {\n return size() >= suffix.size() && compare(size() - suffix.size(), suffix.size(), suffix) == 0;\n }\n\n String replace(char oldChar, char newChar) const {\n std::string r = *this;\n for (auto& c : r) if (c == oldChar) c = newChar;\n return String(r);\n }\n String replace(const std::string& oldStr, const std::string& newStr) const {\n std::string r = *this;\n size_t pos = 0;\n while ((pos = r.find(oldStr, pos)) != npos) {\n r.replace(pos, oldStr.size(), newStr);\n pos += newStr.size();\n }\n return String(r);\n }\n\n String concat(const std::string& other) const { return String(*this + other); }\n\n int compareTo(const std::string& other) const { return compare(other); }\n\n // split(delim) -- one of the most commonly used String methods in\n // real Processing sketches (parsing CSV/delimited text). Matches\n // Java\'s String.split(String regex) for the simple, non-regex,\n // single-character-or-literal-delimiter case. Returns\n // std::vector<String> rather than ArrayList<String> -- ArrayList<T>\n // is declared LATER in this file, so referencing it here would be a\n // forward-reference compile error; std::vector works identically\n // for a simple for-loop over the results and has no ordering\n // dependency.\n // Edge cases handled to match Java\'s actual behavior:\n // - empty input string -> single-element vector containing ""\n // - delimiter not found -> whole string as the only element\n // - consecutive delimiters -> empty-string elements between them\n // (Java does NOT collapse them, and neither do we)\n // - empty delimiter -> returns the original string unsplit\n std::vector<String> split(const std::string& delim) const {\n std::vector<String> result;\n if (delim.empty()) {\n result.push_back(String(*this));\n return result;\n }\n size_t start = 0, pos;\n while ((pos = find(delim, start)) != npos) {\n result.push_back(String(substr(start, pos - start)));\n start = pos + delim.size();\n }\n result.push_back(String(substr(start)));\n return result;\n }\n\n // toCharArray() -- matches Java\'s String.toCharArray(). An empty\n // string correctly returns an empty vector, not a vector containing\n // one null char.\n std::vector<char> toCharArray() const {\n return std::vector<char>(begin(), end());\n }\n\n // ===== Java API additions (added by apply_java_additions.py) =====\n\n // String(char[]) -- round-trip with toCharArray()\n String(const std::vector<char>& chars) : std::string(chars.begin(), chars.end()) {}\n String(const char* chars, size_t count) : std::string(chars, count) {}\n\n // compareToIgnoreCase -- case-insensitive lexicographic compare\n int compareToIgnoreCase(const std::string& other) const {\n size_t n = std::min(size(), other.size());\n for (size_t i = 0; i < n; i++) {\n char a = (char)tolower((unsigned char)(*this)[i]);\n char b = (char)tolower((unsigned char)other[i]);\n if (a != b) return (int)(unsigned char)a - (int)(unsigned char)b;\n }\n return (int)size() - (int)other.size();\n }\n\n // matches()/replaceAll() with regex intentionally NOT implemented --\n // same rationale as split(): real Processing sketches rarely use\n // them, and a correct regex engine is a much bigger addition. Use\n // std::regex directly in sketch code if needed.\n\n // ---- static methods ----\n\n // String.valueOf(...) -- Java\'s universal "stringify a primitive".\n static String valueOf(int v) { return String(std::to_string(v)); }\n static String valueOf(long v) { return String(std::to_string(v)); }\n static String valueOf(float v) { return String(std::to_string(v)); }\n static String valueOf(double v) { return String(std::to_string(v)); }\n static String valueOf(bool v) { return String(v ? "true" : "false"); }\n static String valueOf(char v) { return String(v); }\n static String valueOf(const std::vector<char>& chars) { return String(chars); }\n\n // String.join(delim, ...) -- Java 8+.\n static String join(const std::string& delim, std::initializer_list<std::string> parts) {\n String result;\n bool first = true;\n for (const auto& p : parts) {\n if (!first) result += delim;\n result += p;\n first = false;\n }\n return result;\n }\n template<typename Container>\n static String join(const std::string& delim, const Container& parts) {\n String result;\n bool first = true;\n for (const auto& p : parts) {\n if (!first) result += delim;\n result += p;\n first = false;\n }\n return result;\n }\n\n // String.format(...) -- printf-style formatting matching Java\'s\n // String.format(String, Object...) for the common specifiers real\n // Processing sketches use: %d %f %s %c %x %o %% with width/precision\n // flags (e.g. "%05.2f", "%-10s"). Built on vsnprintf.\n static String format(const char* fmt, ...) {\n va_list args;\n va_start(args, fmt);\n va_list args_copy;\n va_copy(args_copy, args);\n int needed = std::vsnprintf(nullptr, 0, fmt, args_copy);\n va_end(args_copy);\n if (needed < 0) { va_end(args); return String(""); }\n std::vector<char> buf((size_t)needed + 1);\n std::vsnprintf(buf.data(), buf.size(), fmt, args);\n va_end(args);\n return String(std::string(buf.data(), (size_t)needed));\n }\n};\n\n// =============================================================================\n// PRIMITIVE WRAPPER CLASSES -- Integer, Float, Double, Long, Byte, Character\n// =============================================================================\n// Real Java wrapper classes, matching real semantics: a constructor\n// taking the primitive (or a String, parsed via the matching parseXxx\n// logic), an xxxValue() accessor, a static valueOf() factory, a static\n// parseXxx() parser, an implicit conversion operator back to the\n// primitive (covers Java\'s autoboxing/unboxing convenience without\n// needing the user to call xxxValue() everywhere), and toString()/\n// compareTo() for parity with String\'s own wrapper-class methods.\n//\n// Previously these six types were rewritten as plain text to their bare\n// primitive equivalents (Integer->int, Float->float, etc.) -- the same\n// category of bug fixed for String->std::string: it silently changed\n// the user\'s declared type, breaking anything relying on real wrapper-\n// object behavior (valueOf(), parseXxx(), nullability via a sentinel,\n// etc.), even though most everyday Processing code never notices since\n// implicit conversion makes these usable almost everywhere a bare\n// primitive would be.\ntemplate<typename PrimT>\nclass NumberWrapperBase {\nprotected:\n PrimT v;\npublic:\n NumberWrapperBase() : v(PrimT()) {}\n NumberWrapperBase(PrimT val) : v(val) {}\n operator PrimT() const { return v; }\n int compareTo(const NumberWrapperBase& other) const {\n if (v < other.v) return -1;\n if (v > other.v) return 1;\n return 0;\n }\n bool equals(const NumberWrapperBase& other) const { return v == other.v; }\n String toString() const { return String(std::to_string(v)); }\n};\n\nclass Integer : public NumberWrapperBase<int> {\npublic:\n Integer() : NumberWrapperBase<int>() {}\n Integer(int val) : NumberWrapperBase<int>(val) {}\n explicit Integer(const std::string& s) : NumberWrapperBase<int>(std::stoi(s)) {}\n int intValue() const { return v; }\n static Integer valueOf(int val) { return Integer(val); }\n static Integer valueOf(const std::string& s) { return Integer(std::stoi(s)); }\n static int parseInt(const std::string& s) { return std::stoi(s); }\n};\n\nclass Float : public NumberWrapperBase<float> {\npublic:\n Float() : NumberWrapperBase<float>() {}\n Float(float val) : NumberWrapperBase<float>(val) {}\n explicit Float(const std::string& s) : NumberWrapperBase<float>(std::stof(s)) {}\n float floatValue() const { return v; }\n static Float valueOf(float val) { return Float(val); }\n static Float valueOf(const std::string& s) { return Float(std::stof(s)); }\n static float parseFloat(const std::string& s) { return std::stof(s); }\n};\n\nclass Double : public NumberWrapperBase<double> {\npublic:\n Double() : NumberWrapperBase<double>() {}\n Double(double val) : NumberWrapperBase<double>(val) {}\n explicit Double(const std::string& s) : NumberWrapperBase<double>(std::stod(s)) {}\n double doubleValue() const { return v; }\n static Double valueOf(double val) { return Double(val); }\n static Double valueOf(const std::string& s) { return Double(std::stod(s)); }\n static double parseDouble(const std::string& s) { return std::stod(s); }\n};\n\nclass Long : public NumberWrapperBase<long> {\npublic:\n Long() : NumberWrapperBase<long>() {}\n Long(long val) : NumberWrapperBase<long>(val) {}\n explicit Long(const std::string& s) : NumberWrapperBase<long>(std::stol(s)) {}\n long longValue() const { return v; }\n static Long valueOf(long val) { return Long(val); }\n static Long valueOf(const std::string& s) { return Long(std::stol(s)); }\n static long parseLong(const std::string& s) { return std::stol(s); }\n};\n\nclass Byte : public NumberWrapperBase<signed char> {\npublic:\n Byte() : NumberWrapperBase<signed char>() {}\n Byte(signed char val) : NumberWrapperBase<signed char>(val) {}\n explicit Byte(const std::string& s) : NumberWrapperBase<signed char>((signed char)std::stoi(s)) {}\n signed char byteValue() const { return v; }\n static Byte valueOf(signed char val) { return Byte(val); }\n static Byte valueOf(const std::string& s) { return Byte((signed char)std::stoi(s)); }\n static signed char parseByte(const std::string& s) { return (signed char)std::stoi(s); }\n};\n\n// Character is NOT a Number subclass in real Java (it extends Object\n// directly), so it doesn\'t share NumberWrapperBase -- no compareTo via\n// numeric ordering makes sense to inherit from that template here,\n// though char does have a natural ordering, so compareTo is still\n// provided directly.\nclass Character {\n char v;\npublic:\n Character() : v(\'\\0\') {}\n Character(char val) : v(val) {}\n operator char() const { return v; }\n char charValue() const { return v; }\n static Character valueOf(char val) { return Character(val); }\n int compareTo(const Character& other) const {\n if (v < other.v) return -1;\n if (v > other.v) return 1;\n return 0;\n }\n bool equals(const Character& other) const { return v == other.v; }\n String toString() const { return String(std::string(1, v)); }\n static bool isDigit(char c) { return c >= \'0\' && c <= \'9\'; }\n static bool isLetter(char c) { return std::isalpha((unsigned char)c) != 0; }\n static bool isUpperCase(char c) { return std::isupper((unsigned char)c) != 0; }\n static bool isLowerCase(char c) { return std::islower((unsigned char)c) != 0; }\n static char toUpperCase(char c) { return (char)std::toupper((unsigned char)c); }\n static char toLowerCase(char c) { return (char)std::tolower((unsigned char)c); }\n};\n\n\nclass IntList {\npublic:\n std::vector<int> data;\n IntList() = default;\n IntList(std::initializer_list<int> l) : data(l) {}\n // Java-style\n void append(int v) { data.push_back(v); }\n void add(int v) { data.push_back(v); }\n void add(int i, int v) { data.insert(data.begin()+i,v); }\n void set(int i, int v) { data[i]=v; }\n int get(int i) const { return data[i]; }\n int size() const { return (int)data.size(); }\n bool isEmpty() const { return data.empty(); }\n void sort() { std::sort(data.begin(),data.end()); }\n void reverse() { std::reverse(data.begin(),data.end()); }\n bool hasValue(int v) const { return std::find(data.begin(),data.end(),v)!=data.end(); }\n bool contains(int v) const { return hasValue(v); }\n void remove(int i) { data.erase(data.begin()+i); }\n void clear() { data.clear(); }\n void shuffle() {\n for(int i=(int)data.size()-1;i>0;i--){\n int j=rand()%(i+1); std::swap(data[i],data[j]);\n }\n }\n int& operator[](int i) { return data[i]; }\n auto begin() { return data.begin(); }\n auto end() { return data.end(); }\n\n // ===== Java/Processing API additions (apply_java_additions.py) =====\n explicit IntList(int length) : data((size_t)std::max(0, length), 0) {}\n\n static IntList fromRange(int stop) { return fromRange(0, stop); }\n static IntList fromRange(int start, int stop) {\n IntList r;\n for (int i = start; i < stop; i++) r.append(i);\n return r;\n }\n\n void resize(int length) { data.resize((size_t)std::max(0, length), 0); }\n\n void push(int v) { append(v); }\n int pop() {\n if (data.empty()) throw std::runtime_error("Can\'t call pop() on an empty list");\n int v = data.back();\n data.pop_back();\n return v;\n }\n\n int index(int value) const {\n for (int i = 0; i < (int)data.size(); i++) if (data[i] == value) return i;\n return -1;\n }\n int removeValue(int value) {\n int idx = index(value);\n if (idx != -1) remove(idx);\n return idx;\n }\n int removeValues(int value) {\n int before = (int)data.size();\n data.erase(std::remove(data.begin(), data.end(), value), data.end());\n return before - (int)data.size();\n }\n void appendUnique(int value) { if (!hasValue(value)) append(value); }\n\n void append(const std::vector<int>& values) { for (int v : values) append(v); }\n void append(const IntList& list) {\n // Snapshot first: if \'list\' is THIS SAME object (e.g. self-aliasing\n // call list.append(list)), iterating list.data directly while\n // append(v) grows this->data via push_back can reallocate the\n // underlying buffer mid-loop, invalidating the range-for\'s\n // captured begin()/end() iterators -- a use-after-free. Copying\n // values out first makes append(self) safe and correct.\n // (Found by stress-testing; confirmed via AddressSanitizer.)\n std::vector<int> snapshot = list.data;\n for (int v : snapshot) append(v);\n }\n\n void increment(int idx) {\n if ((int)data.size() <= idx) resize(idx + 1);\n data[idx]++;\n }\n // addAt/subAt/multAt/divAt -- real Java IntList overloads add() etc.\n // for in-place arithmetic on one element using the SAME name as\n // insert (add(index,value)); we use distinct names here since our\n // add(int,int) already means "insert v at i".\n void addAt(int idx, int amount) { data.at(idx) += amount; }\n void subAt(int idx, int amount) { data.at(idx) -= amount; }\n void multAt(int idx, int amount) { data.at(idx) *= amount; }\n void divAt(int idx, int amount) { data.at(idx) /= amount; }\n\n int min() const {\n if (data.empty()) throw std::runtime_error("Cannot use min() on an empty IntList.");\n return *std::min_element(data.begin(), data.end());\n }\n int max() const {\n if (data.empty()) throw std::runtime_error("Cannot use max() on an empty IntList.");\n return *std::max_element(data.begin(), data.end());\n }\n int minIndex() const {\n if (data.empty()) throw std::runtime_error("Cannot use minIndex() on an empty IntList.");\n return (int)std::distance(data.begin(), std::min_element(data.begin(), data.end()));\n }\n int maxIndex() const {\n if (data.empty()) throw std::runtime_error("Cannot use maxIndex() on an empty IntList.");\n return (int)std::distance(data.begin(), std::max_element(data.begin(), data.end()));\n }\n long sumLong() const { long s = 0; for (int v : data) s += v; return s; }\n int sum() const { return (int)sumLong(); }\n\n void sortReverse() { std::sort(data.begin(), data.end(), std::greater<int>()); }\n\n IntList copy() const { IntList r; r.data = data; return r; }\n\n IntList getSubset(int start) const { return getSubset(start, (int)data.size() - start); }\n IntList getSubset(int start, int num) const {\n // Real Java IntList.getSubset() relies on System.arraycopy, which\n // throws for an out-of-range start/num. Our begin()+start+num\n // iterator arithmetic is UB if out of range rather than a safe\n // throw -- confirmed by AddressSanitizer -- so we validate first.\n if (start < 0 || num < 0 || start + num > (int)data.size()) {\n throw std::out_of_range("IntList::getSubset() index out of range");\n }\n IntList r;\n r.data.assign(data.begin() + start, data.begin() + start + num);\n return r;\n }\n\n std::string join(const std::string& separator) const {\n if (data.empty()) return "";\n std::string r = std::to_string(data[0]);\n for (size_t i = 1; i < data.size(); i++) { r += separator; r += std::to_string(data[i]); }\n return r;\n }\n void print() const {\n for (int i = 0; i < (int)data.size(); i++) printf("[%d] %d\\n", i, data[i]);\n }\n std::string toString() const {\n return "IntList size=" + std::to_string(size()) + " [ " + join(", ") + " ]";\n }\n};\n\nclass FloatList {\npublic:\n std::vector<float> data;\n FloatList() = default;\n FloatList(std::initializer_list<float> l) : data(l) {}\n void append(float v) { data.push_back(v); }\n void add(float v) { data.push_back(v); }\n void set(int i, float v) { data[i]=v; }\n float get(int i) const { return data[i]; }\n int size() const { return (int)data.size(); }\n bool isEmpty() const { return data.empty(); }\n void sort() { std::sort(data.begin(),data.end()); }\n void reverse() { std::reverse(data.begin(),data.end()); }\n void remove(int i) { data.erase(data.begin()+i); }\n void clear() { data.clear(); }\n void shuffle() {\n for(int i=(int)data.size()-1;i>0;i--){\n int j=rand()%(i+1); std::swap(data[i],data[j]);\n }\n }\n float& operator[](int i) { return data[i]; }\n auto begin() { return data.begin(); }\n auto end() { return data.end(); }\n\n // ===== Java/Processing API additions (apply_java_additions.py) =====\n explicit FloatList(int length) : data((size_t)std::max(0, length), 0.0f) {}\n\n void resize(int length) { data.resize((size_t)std::max(0, length), 0.0f); }\n\n void add(int i, float v) { data.insert(data.begin()+i, v); }\n\n bool hasValue(float v) const { return std::find(data.begin(),data.end(),v)!=data.end(); }\n bool contains(float v) const { return hasValue(v); }\n\n void push(float v) { append(v); }\n float pop() {\n if (data.empty()) throw std::runtime_error("Can\'t call pop() on an empty list");\n float v = data.back();\n data.pop_back();\n return v;\n }\n\n int index(float value) const {\n for (int i = 0; i < (int)data.size(); i++) if (data[i] == value) return i;\n return -1;\n }\n int removeValue(float value) {\n int idx = index(value);\n if (idx != -1) remove(idx);\n return idx;\n }\n int removeValues(float value) {\n int before = (int)data.size();\n data.erase(std::remove(data.begin(), data.end(), value), data.end());\n return before - (int)data.size();\n }\n void appendUnique(float value) { if (!hasValue(value)) append(value); }\n\n void append(const std::vector<float>& values) { for (float v : values) append(v); }\n void append(const FloatList& list) {\n // Snapshot first -- see IntList::append(const IntList&) comment;\n // protects against self-aliasing (list.append(list)) reallocating\n // mid-iteration and invalidating the iterators we\'re reading from.\n std::vector<float> snapshot = list.data;\n for (float v : snapshot) append(v);\n }\n\n void addAt(int idx, float amount) { data.at(idx) += amount; }\n void subAt(int idx, float amount) { data.at(idx) -= amount; }\n void multAt(int idx, float amount) { data.at(idx) *= amount; }\n void divAt(int idx, float amount) { data.at(idx) /= amount; }\n\n float min() const {\n if (data.empty()) throw std::runtime_error("Cannot use min() on an empty FloatList.");\n return *std::min_element(data.begin(), data.end());\n }\n float max() const {\n if (data.empty()) throw std::runtime_error("Cannot use max() on an empty FloatList.");\n return *std::max_element(data.begin(), data.end());\n }\n int minIndex() const {\n if (data.empty()) throw std::runtime_error("Cannot use minIndex() on an empty FloatList.");\n return (int)std::distance(data.begin(), std::min_element(data.begin(), data.end()));\n }\n int maxIndex() const {\n if (data.empty()) throw std::runtime_error("Cannot use maxIndex() on an empty FloatList.");\n return (int)std::distance(data.begin(), std::max_element(data.begin(), data.end()));\n }\n double sum() const { double s = 0; for (float v : data) s += v; return s; }\n\n void sortReverse() { std::sort(data.begin(), data.end(), std::greater<float>()); }\n\n FloatList copy() const { FloatList r; r.data = data; return r; }\n\n FloatList getSubset(int start) const { return getSubset(start, (int)data.size() - start); }\n FloatList getSubset(int start, int num) const {\n // See IntList::getSubset() comment -- same UB risk, same fix.\n if (start < 0 || num < 0 || start + num > (int)data.size()) {\n throw std::out_of_range("FloatList::getSubset() index out of range");\n }\n FloatList r;\n r.data.assign(data.begin() + start, data.begin() + start + num);\n return r;\n }\n\n std::string join(const std::string& separator) const {\n if (data.empty()) return "";\n std::string r = std::to_string(data[0]);\n for (size_t i = 1; i < data.size(); i++) { r += separator; r += std::to_string(data[i]); }\n return r;\n }\n void print() const {\n for (int i = 0; i < (int)data.size(); i++) printf("[%d] %f\\n", i, data[i]);\n }\n std::string toString() const {\n return "FloatList size=" + std::to_string(size()) + " [ " + join(", ") + " ]";\n }\n};\n\nclass StringList {\npublic:\n std::vector<std::string> data;\n StringList() = default;\n StringList(std::initializer_list<std::string> l) : data(l) {}\n void append(const std::string& v) { data.push_back(v); }\n void set(int i, const std::string& v){ data[i]=v; }\n std::string get(int i) const { return data[i]; }\n int size() const { return (int)data.size(); }\n void sort() { std::sort(data.begin(),data.end()); }\n void reverse() { std::reverse(data.begin(),data.end()); }\n bool hasValue(const std::string& v) const { return std::find(data.begin(),data.end(),v)!=data.end(); }\n void remove(int i) { data.erase(data.begin()+i); }\n void clear() { data.clear(); }\n std::string& operator[](int i) { return data[i]; }\n\n // ===== Java/Processing API additions (apply_java_additions.py) =====\n explicit StringList(int length) : data((size_t)std::max(0, length)) {}\n\n bool isEmpty() const { return data.empty(); }\n void resize(int length) { data.resize((size_t)std::max(0, length)); }\n\n void add(const std::string& v) { data.push_back(v); }\n void add(int i, const std::string& v) { data.insert(data.begin()+i, v); }\n bool contains(const std::string& v) const { return hasValue(v); }\n\n void push(const std::string& v) { append(v); }\n std::string pop() {\n if (data.empty()) throw std::runtime_error("Can\'t call pop() on an empty list");\n std::string v = data.back();\n data.pop_back();\n return v;\n }\n\n int index(const std::string& value) const {\n for (int i = 0; i < (int)data.size(); i++) if (data[i] == value) return i;\n return -1;\n }\n int removeValue(const std::string& value) {\n int idx = index(value);\n if (idx != -1) remove(idx);\n return idx;\n }\n int removeValues(const std::string& value) {\n int before = (int)data.size();\n data.erase(std::remove(data.begin(), data.end(), value), data.end());\n return before - (int)data.size();\n }\n void appendUnique(const std::string& value) { if (!hasValue(value)) append(value); }\n\n void append(const std::vector<std::string>& values) { for (auto& v : values) append(v); }\n void append(const StringList& list) {\n // Snapshot first -- see IntList::append(const IntList&) comment;\n // protects against self-aliasing (list.append(list)) reallocating\n // mid-iteration and invalidating the iterators we\'re reading from.\n std::vector<std::string> snapshot = list.data;\n for (auto& v : snapshot) append(v);\n }\n\n void shuffle() {\n for (int i = (int)data.size()-1; i > 0; i--) {\n int j = rand() % (i+1);\n std::swap(data[i], data[j]);\n }\n }\n\n StringList copy() const { StringList r; r.data = data; return r; }\n\n StringList getSubset(int start) const { return getSubset(start, (int)data.size() - start); }\n StringList getSubset(int start, int num) const {\n // See IntList::getSubset() comment -- same UB risk, same fix.\n if (start < 0 || num < 0 || start + num > (int)data.size()) {\n throw std::out_of_range("StringList::getSubset() index out of range");\n }\n StringList r;\n r.data.assign(data.begin() + start, data.begin() + start + num);\n return r;\n }\n\n std::string join(const std::string& separator) const {\n if (data.empty()) return "";\n std::string r = data[0];\n for (size_t i = 1; i < data.size(); i++) { r += separator; r += data[i]; }\n return r;\n }\n void print() const {\n for (int i = 0; i < (int)data.size(); i++) printf("[%d] %s\\n", i, data[i].c_str());\n }\n std::string toString() const {\n return "StringList size=" + std::to_string(size()) + " [ " + join(", ") + " ]";\n }\n};\n\n// =============================================================================\n// ArrayList<T> -- Java-style generic list, backed by std::vector. Real\n// Processing/Java method names (add/get/remove/size/isEmpty/contains),\n// NOT a textual rewrite to std::vector, since std::vector\'s own method\n// names (push_back/operator[]/erase) don\'t match what sketch source\n// written against Java\'s ArrayList API actually calls.\n//\n// In Java, ArrayList<T> ALWAYS stores references for object types --\n// there are no value-type objects in Java at all, so this was never a\n// choice the sketch author made, it\'s just how every Java object type\n// behaves. PImage/PFont/PShape/PGraphics specifically are non-copyable in\n// our C++ port (they own unique GPU resources -- copying one would either\n// crash via double-free or silently alias the same resource from two\n// "different" objects), which is the correct C++ translation of "this is\n// reference-like in Java." Rather than forcing sketch authors to notice\n// and write ArrayList<PGraphics*> for exactly these four types, ArrayList\n// detects non-copy-constructible T automatically and stores T* internally\n// -- the PUBLIC API (add/get/etc.) still looks and behaves like Java\'s,\n// the indirection is an implementation detail, exactly mirroring how\n// "PGraphics pg; pg = createGraphics(...);" already becomes a pointer\n// under the hood without the sketch author needing to write one.\n// Default rule: T is reference-like (stored as T*) UNLESS it\'s one of\n// Java\'s true value types -- primitives (int/float/bool/char/etc.) or\n// std::string/String. This matches Java\'s ACTUAL semantics exactly:\n// every object/class type in Java is reference-like, full stop --\n// primitives are the only exception. Our earlier version used\n// "!std::is_copy_constructible<T>" as the trigger, which correctly\n// caught PImage/PFont/PShape/PGraphics (non-copyable in C++, so they\n// were forced to be pointer-stored) but WRONGLY left ordinary,\n// copyable user-defined classes (e.g. a sketch\'s own "Particle" class)\n// as value-stored -- meaning ArrayList<Particle>.get(i) returned a\n// COPY, so calling p.update() on that copy never mutated what was\n// actually stored in the list. In Java, Particle is reference-like\n// =============================================================================\n// Array<T> -- fixed-size array matching real Java array semantics\n// =============================================================================\n// Java\'s "int[] a = new int[10];" creates a FIXED-SIZE array: zero/false/\n// null-initialized by default, and bounds-checked at runtime (throwing\n// ArrayIndexOutOfBoundsException on an invalid index) -- this is\n// DIFFERENT from ArrayList<T> (growable, add()/remove()) and different\n// from a raw C-style array (no bounds checking at all in C++, undefined\n// behavior on out-of-range access rather than a clean, catchable error).\n//\n// Array<T> exists to be the faithful, safe translation of THIS specific\n// Java construct: fixed length (set once, at construction, matching\n// Java\'s own "can\'t resize an array" rule), default-initialized\n// elements, and a bounds-checked [] operator that throws std::out_of_range\n// (the closest C++ equivalent to Java\'s ArrayIndexOutOfBoundsException)\n// rather than silently reading/writing out-of-bounds memory.\n//\n// Real Java syntax ("int[] a = new int[10];") is intentionally NOT\n// supported directly -- see the E0004 compiler error attached to the\n// blocked overload below. CppBuild does not attempt to silently rewrite\n// this syntax, for the same reason pointerizeNewAssignedVars was\n// removed earlier: guessing at the user\'s intent via text rewriting is\n// exactly the category of fragile, comment-blind, scope-unaware\n// mechanism this whole session has been moving away from. Sketch\n// authors write Array<T> explicitly instead.\ntemplate<typename T>\nclass Array {\n std::vector<T> data;\npublic:\n explicit Array(int size) : data(size > 0 ? (size_t)size : 0, T()) {}\n Array(int size, const T& fillValue) : data(size > 0 ? (size_t)size : 0, fillValue) {}\n\n int length() const { return (int)data.size(); }\n\n // Bounds-checked access -- matches Java\'s ArrayIndexOutOfBoundsException\n // semantics (a clean, catchable error) rather than C++\'s usual\n // undefined-behavior-on-out-of-range for operator[].\n T& operator[](int index) {\n if (index < 0 || index >= (int)data.size())\n throw std::out_of_range(\n "Array index " + std::to_string(index) +\n " out of bounds for length " + std::to_string(data.size()));\n return data[(size_t)index];\n }\n const T& operator[](int index) const {\n if (index < 0 || index >= (int)data.size())\n throw std::out_of_range(\n "Array index " + std::to_string(index) +\n " out of bounds for length " + std::to_string(data.size()));\n return data[(size_t)index];\n }\n\n auto begin() { return data.begin(); }\n auto end() { return data.end(); }\n auto begin() const { return data.begin(); }\n auto end() const { return data.end(); }\n\n // Matches Java\'s array-literal syntax: int[] a = {1, 2, 3};\n Array(std::initializer_list<T> init) : data(init) {}\n\n // Exposed so the free functions below (append/arrayCopy/concat/\n // expand/reverse/shorten/sort/splice/subset) can build new Array<T>\n // instances directly from a std::vector<T>.\n static Array<T> fromVector(std::vector<T> v) {\n Array<T> a(0);\n a.data = std::move(v);\n return a;\n }\n const std::vector<T>& rawData() const { return data; }\n std::vector<T>& rawData() { return data; }\n};\n\n// =============================================================================\n// Array<T> utility functions -- matching real Processing\'s free-function\n// call style exactly: "arr = append(arr, val);" not "arr.append(val);",\n// since every one of these (except reverse/sort) returns a NEW array\n// rather than mutating in place, mirroring Java\'s own fixed-length-array\n// constraint.\n// =============================================================================\n\ntemplate<typename T>\nArray<T> append(const Array<T>& arr, const T& value) {\n std::vector<T> v = arr.rawData();\n v.push_back(value);\n return Array<T>::fromVector(std::move(v));\n}\n\ntemplate<typename T>\nvoid arrayCopy(const Array<T>& src, Array<T>& dst) {\n int n = std::min(src.length(), dst.length());\n for (int i = 0; i < n; i++) dst[i] = src[i];\n}\ntemplate<typename T>\nvoid arrayCopy(const Array<T>& src, int srcPos, Array<T>& dst, int dstPos, int length) {\n for (int i = 0; i < length; i++) dst[dstPos + i] = src[srcPos + i];\n}\n\ntemplate<typename T>\nArray<T> concat(const Array<T>& a, const Array<T>& b) {\n std::vector<T> v = a.rawData();\n const auto& bv = b.rawData();\n v.insert(v.end(), bv.begin(), bv.end());\n return Array<T>::fromVector(std::move(v));\n}\n\ntemplate<typename T>\nArray<T> expand(const Array<T>& arr) {\n int newSize = arr.length() == 0 ? 1 : arr.length() * 2;\n std::vector<T> v = arr.rawData();\n v.resize((size_t)newSize, T());\n return Array<T>::fromVector(std::move(v));\n}\ntemplate<typename T>\nArray<T> expand(const Array<T>& arr, int newSize) {\n std::vector<T> v = arr.rawData();\n v.resize((size_t)std::max(newSize, arr.length()), T());\n return Array<T>::fromVector(std::move(v));\n}\n\ntemplate<typename T>\nvoid reverse(Array<T>& arr) {\n std::reverse(arr.rawData().begin(), arr.rawData().end());\n}\n\ntemplate<typename T>\nArray<T> shorten(const Array<T>& arr) {\n std::vector<T> v = arr.rawData();\n if (!v.empty()) v.pop_back();\n return Array<T>::fromVector(std::move(v));\n}\n\ntemplate<typename T>\nvoid sort(Array<T>& arr) {\n std::sort(arr.rawData().begin(), arr.rawData().end());\n}\ntemplate<typename T>\nvoid sort(Array<T>& arr, int count) {\n std::sort(arr.rawData().begin(), arr.rawData().begin() + std::min(count, arr.length()));\n}\n\ntemplate<typename T>\nArray<T> splice(const Array<T>& arr, const T& value, int index) {\n std::vector<T> v = arr.rawData();\n v.insert(v.begin() + index, value);\n return Array<T>::fromVector(std::move(v));\n}\ntemplate<typename T>\nArray<T> splice(const Array<T>& arr, const Array<T>& values, int index) {\n std::vector<T> v = arr.rawData();\n const auto& vv = values.rawData();\n v.insert(v.begin() + index, vv.begin(), vv.end());\n return Array<T>::fromVector(std::move(v));\n}\n\ntemplate<typename T>\nArray<T> subset(const Array<T>& arr, int start) {\n const auto& v = arr.rawData();\n return Array<T>::fromVector(std::vector<T>(v.begin() + start, v.end()));\n}\ntemplate<typename T>\nArray<T> subset(const Array<T>& arr, int start, int count) {\n const auto& v = arr.rawData();\n return Array<T>::fromVector(std::vector<T>(v.begin() + start, v.begin() + start + count));\n}\n\nclass Integer; class Float; class Double; class Long; class Byte; class Character;\ntemplate<typename T>\nstruct IsJavaValueType : std::integral_constant<bool,\n std::is_arithmetic<T>::value || // int, float, double, bool, char, etc.\n std::is_same<T, std::string>::value ||\n std::is_same<T, String>::value ||\n // BUG FIX: the new Integer/Float/Double/Long/Byte/Character wrapper\n // classes are NOT std::is_arithmetic (they\'re classes wrapping a\n // primitive, not primitives themselves), so without this explicit\n // list, ArrayList<Integer> etc. would silently become reference-\n // storage (Integer*) -- breaking "nums.add(10);" (a plain int\n // literal can\'t implicitly become an Integer*) even though these\n // wrapper classes are specifically designed to be lightweight,\n // copyable stand-ins for primitives (unlike PImage/PGraphics, which\n // genuinely own unique GPU resources and must stay reference-like).\n std::is_same<T, Integer>::value ||\n std::is_same<T, Float>::value ||\n std::is_same<T, Double>::value ||\n std::is_same<T, Long>::value ||\n std::is_same<T, Byte>::value ||\n std::is_same<T, Character>::value\n> {};\n\ntemplate<typename T, bool IsRefLike = !IsJavaValueType<T>::value>\nclass ArrayList;\n\n// Value-storage specialization: used for ordinary copyable types\n// (int, float, String, user structs without unique GPU resources, etc.)\ntemplate<typename T>\nclass ArrayList<T, false> {\npublic:\n std::vector<T> data;\n ArrayList() = default;\n ArrayList(std::initializer_list<T> l) : data(l) {}\n void add(const T& v) { data.push_back(v); }\n void add(int i, const T& v) { data.insert(data.begin()+i, v); }\n void set(int i, const T& v) { data[i]=v; }\n T get(int i) const{ return data[i]; }\n int size() const{ return (int)data.size(); }\n bool isEmpty() const{ return data.empty(); }\n bool contains(const T& v) const{ return std::find(data.begin(),data.end(),v)!=data.end(); }\n void remove(int i) { data.erase(data.begin()+i); }\n void clear() { data.clear(); }\n T& operator[](int i) { return data[i]; }\n auto begin() { return data.begin(); }\n auto end() { return data.end(); }\n\n // ===== java.util.ArrayList<T> API additions (apply_java_additions.py) =====\n bool removeElement(const T& v) {\n auto it = std::find(data.begin(), data.end(), v);\n if (it == data.end()) return false;\n data.erase(it);\n return true;\n }\n int indexOf(const T& v) const {\n auto it = std::find(data.begin(), data.end(), v);\n return it == data.end() ? -1 : (int)std::distance(data.begin(), it);\n }\n int lastIndexOf(const T& v) const {\n auto it = std::find(data.rbegin(), data.rend(), v);\n return it == data.rend() ? -1 : (int)(data.size() - 1 - std::distance(data.rbegin(), it));\n }\n void addAll(const ArrayList<T,false>& other) {\n // Snapshot first: protects against self-aliasing (list.addAll(list)).\n // vector::insert with a source range that overlaps the destination\n // vector is not guaranteed safe by the standard if the insert\n // triggers reallocation -- copying out first avoids relying on\n // implementation-specific behavior. (Found by stress-testing.)\n std::vector<T> snapshot = other.data;\n data.insert(data.end(), snapshot.begin(), snapshot.end());\n }\n void addAll(int idx, const ArrayList<T,false>& other) {\n std::vector<T> snapshot = other.data;\n data.insert(data.begin()+idx, snapshot.begin(), snapshot.end());\n }\n ArrayList<T,false> subList(int from, int to) const {\n // Real java.util.ArrayList.subList() throws IndexOutOfBoundsException\n // for fromIndex<0, toIndex>size(), or fromIndex>toIndex. Without this\n // check, data.begin()+from or data.begin()+to with an out-of-range\n // offset is undefined behavior in the STL (not a safe throw) --\n // confirmed by AddressSanitizer during stress-testing.\n if (from < 0 || to > (int)data.size() || from > to) {\n throw std::out_of_range("ArrayList::subList() index out of range");\n }\n ArrayList<T,false> r;\n r.data.assign(data.begin()+from, data.begin()+to);\n return r;\n }\n void ensureCapacity(int cap) { data.reserve((size_t)cap); }\n void trimToSize() { data.shrink_to_fit(); }\n};\n\n// Reference-storage specialization: used automatically for non-copyable\n// T (PImage, PFont, PShape, PGraphics). Stores T* internally; the public\n// API still takes/returns in terms that match how Java code calls it --\n// add() takes a T* (matching what createGraphics()/loadImage() etc.\n// already return), get() returns T* (matching Java\'s reference\n// semantics: "PGraphics pg = list.get(i);" should give you the SAME\n// object, not a copy).\ntemplate<typename T>\nclass ArrayList<T, true> {\npublic:\n std::vector<T*> data;\n ArrayList() = default;\n void add(T* v) { data.push_back(v); }\n void add(int i, T* v) { data.insert(data.begin()+i, v); }\n void set(int i, T* v) { data[i]=v; }\n T* get(int i) const{ return data[i]; }\n int size() const{ return (int)data.size(); }\n bool isEmpty() const{ return data.empty(); }\n bool contains(T* v) const{ return std::find(data.begin(),data.end(),v)!=data.end(); }\n void remove(int i) { data.erase(data.begin()+i); }\n void clear() { data.clear(); }\n T*& operator[](int i) { return data[i]; }\n auto begin() { return data.begin(); }\n auto end() { return data.end(); }\n\n // ===== java.util.ArrayList<T> API additions (apply_java_additions.py) =====\n bool removeElement(T* v) {\n auto it = std::find(data.begin(), data.end(), v);\n if (it == data.end()) return false;\n data.erase(it);\n return true;\n }\n int indexOf(T* v) const {\n auto it = std::find(data.begin(), data.end(), v);\n return it == data.end() ? -1 : (int)std::distance(data.begin(), it);\n }\n int lastIndexOf(T* v) const {\n auto it = std::find(data.rbegin(), data.rend(), v);\n return it == data.rend() ? -1 : (int)(data.size() - 1 - std::distance(data.rbegin(), it));\n }\n void addAll(const ArrayList<T,true>& other) {\n // Snapshot first -- see ArrayList<T,false>::addAll comment.\n std::vector<T*> snapshot = other.data;\n data.insert(data.end(), snapshot.begin(), snapshot.end());\n }\n void addAll(int idx, const ArrayList<T,true>& other) {\n std::vector<T*> snapshot = other.data;\n data.insert(data.begin()+idx, snapshot.begin(), snapshot.end());\n }\n ArrayList<T,true> subList(int from, int to) const {\n // See ArrayList<T,false>::subList() comment -- same UB risk,\n // same fix.\n if (from < 0 || to > (int)data.size() || from > to) {\n throw std::out_of_range("ArrayList::subList() index out of range");\n }\n ArrayList<T,true> r;\n r.data.assign(data.begin()+from, data.begin()+to);\n return r;\n }\n void ensureCapacity(int cap) { data.reserve((size_t)cap); }\n void trimToSize() { data.shrink_to_fit(); }\n};\n\n// =============================================================================\n// PMap<K,V> -- thin std::unordered_map wrapper\ntemplate<typename K, typename V>\nclass PMap {\npublic:\n ::std::unordered_map<K,V> _data;\n void put(const K& k, const V& v) { _data[k]=v; }\n V& get(const K& k) { return _data[k]; }\n bool containsKey(const K& k) const { return _data.count(k)>0; }\n void remove(const K& k) { _data.erase(k); }\n int size() const { return (int)_data.size(); }\n void clear() { _data.clear(); }\n auto begin() { return _data.begin(); }\n auto end() { return _data.end(); }\n};\n\n// =============================================================================\n// PGraphics method implementations (after all Processing function declarations)\n// =============================================================================\n\nclass IntDict {\npublic:\n std::map<std::string,int> data;\n void set(const std::string& k, int v) { data[k]=v; }\n int get(const std::string& k, int def=0) const { auto it=data.find(k); return it!=data.end()?it->second:def; }\n bool hasKey(const std::string& k) const { return data.count(k)>0; }\n void remove(const std::string& k) { data.erase(k); }\n int size() const { return (int)data.size(); }\n void clear() { data.clear(); }\n std::vector<std::string> keys() const { std::vector<std::string> r; for(auto& p:data) r.push_back(p.first); return r; }\n int& operator[](const std::string& k) { return data[k]; }\n};\n\nclass FloatDict {\npublic:\n std::map<std::string,float> data;\n void set(const std::string& k, float v) { data[k]=v; }\n float get(const std::string& k, float def=0) const { auto it=data.find(k); return it!=data.end()?it->second:def; }\n bool hasKey(const std::string& k) const { return data.count(k)>0; }\n void remove(const std::string& k) { data.erase(k); }\n int size() const { return (int)data.size(); }\n void clear() { data.clear(); }\n std::vector<std::string> keys() const { std::vector<std::string> r; for(auto& p:data) r.push_back(p.first); return r; }\n float& operator[](const std::string& k) { return data[k]; }\n};\n\nclass StringDict {\npublic:\n std::map<std::string,std::string> data;\n void set(const std::string& k, const std::string& v) { data[k]=v; }\n std::string get(const std::string& k, const std::string& def="") const { auto it=data.find(k); return it!=data.end()?it->second:def; }\n bool hasKey(const std::string& k) const { return data.count(k)>0; }\n void remove(const std::string& k) { data.erase(k); }\n int size() const { return (int)data.size(); }\n void clear() { data.clear(); }\n std::vector<std::string> keys() const { std::vector<std::string> r; for(auto& p:data) r.push_back(p.first); return r; }\n std::string& operator[](const std::string& k) { return data[k]; }\n};\n\n// =============================================================================\n// PSHAPE -- reusable geometry (created with createShape / loadShape)\n// =============================================================================\n\nclass PShape {\npublic:\n struct Vertex { float x,y,z,u,v; };\n\n std::vector<Vertex> verts;\n std::vector<PShape> children;\n int kind = -1;\n bool closed = false;\n bool visible = true;\n\n float fillR=1,fillG=1,fillB=1,fillA=1;\n float strokeR=0,strokeG=0,strokeB=0,strokeA=1,strokeW=1;\n bool hasFill=true, hasStroke=false;\n\n PShape() = default;\n explicit PShape(int k) : kind(k) {}\n // Allow PShape bot = loadShape("file.svg") -- copies from pointer\n PShape(const PShape* p) { if(p) *this = *p; }\n PShape& operator=(const PShape* p) { if(p) *this = *p; return *this; }\n\n void beginShape(int k=-1) { kind=k; verts.clear(); }\n void endShape(bool close=false) { closed=close; }\n void vertex(float x,float y,float z=0,float u=0,float v=0) { verts.push_back({x,y,z,u,v}); }\n void addChild(const PShape& s) { children.push_back(s); }\n std::string name; // id/name attribute from SVG\n std::vector<int> subpathStarts; // subpath start indices for multi-part fills\n std::vector<Vertex> anchorVerts; // raw anchor points (M/L/C endpoints only) for getVertex()\n\n PShape* getChild(int i) { return i<(int)children.size()?&children[i]:nullptr; }\n PShape* getChild(const std::string& n) {\n for(auto& c:children) if(c.name==n) return &c;\n for(auto& c:children){ PShape* r=c.getChild(n); if(r) return r; }\n // Return a static empty shape rather than nullptr to prevent crashes\n static PShape _empty;\n fprintf(stderr,"[PShape] getChild(\'%s\') not found\\n", n.c_str());\n return &_empty;\n }\n PShape* getChild(const char* n) { return getChild(std::string(n)); }\n int getChildCount() const { return (int)children.size(); }\n PVector getVertex(int i) const {\n if(i<0||i>=(int)verts.size()) return PVector(0,0,0);\n return PVector(verts[i].x, verts[i].y, verts[i].z);\n }\n void setVertex(int i, float x, float y) {\n if(i>=0&&i<(int)verts.size()){verts[i].x=x;verts[i].y=y;}\n }\n void setVertex(int i, float x, float y, float z) {\n if(i>=0&&i<(int)verts.size()){verts[i].x=x;verts[i].y=y;verts[i].z=z;}\n }\n\n // Bounding box (computed from verts + children)\n float width = 0;\n float height = 0;\n void computeBounds() {\n float minx=1e9,maxx=-1e9,miny=1e9,maxy=-1e9;\n for(auto& v:verts){minx=std::min(minx,v.x);maxx=std::max(maxx,v.x);miny=std::min(miny,v.y);maxy=std::max(maxy,v.y);}\n for(auto& c:children){const_cast<PShape&>(c).computeBounds();minx=std::min(minx,c.verts.empty()?minx:minx);\n if(!c.verts.empty()){for(auto& v:c.verts){minx=std::min(minx,v.x);maxx=std::max(maxx,v.x);miny=std::min(miny,v.y);maxy=std::max(maxy,v.y);}}}\n width =(maxx>-1e8)?(maxx-minx):0;\n height=(maxy>-1e8)?(maxy-miny):0;\n }\n int getVertexCount() const { return (int)verts.size(); }\n\n void setFill(float r,float g,float b,float a=1) { fillR=r;fillG=g;fillB=b;fillA=a;hasFill=true; }\n void setStroke(float r,float g,float b,float a=1) { strokeR=r;strokeG=g;strokeB=b;strokeA=a;hasStroke=true; }\n void setStrokeWeight(float w) { strokeW=w; }\n void setVisible(bool v) { visible=v; }\n\n void translate(float x,float y,float z=0) { for(auto& v:verts){ v.x+=x;v.y+=y;v.z+=z; } }\n void scale(float s) { for(auto& v:verts){ v.x*=s;v.y*=s;v.z*=s; } }\n void scale(float sx, float sy) { for(auto& v:verts){ v.x*=sx;v.y*=sy; } }\n\n // Style enable/disable -- controls whether shape uses its own fill/stroke\n // or inherits from the current Processing fill()/stroke() state\n bool styleEnabled = true;\n void disableStyle() { styleEnabled = false; }\n void enableStyle() { styleEnabled = true; }\n GLuint texId = 0; // OpenGL texture ID for OBJ material textures\n};\n\n\n// =============================================================================\n// PFONT\n// =============================================================================\n\nstruct PFont {\n static std::vector<std::string> list() {\n std::vector<std::string> fonts;\n std::vector<std::string> dirs = {"data",".","/usr/share/fonts","/usr/local/share/fonts"};\n #ifdef _WIN32\n dirs.push_back("C:/Windows/Fonts");\n #elif defined(__APPLE__)\n dirs.push_back("/Library/Fonts"); dirs.push_back("/System/Library/Fonts");\n #endif\n if(getenv("HOME")){ dirs.push_back(std::string(getenv("HOME"))+"/.fonts"); }\n std::function<void(const std::string&)> scan=[&](const std::string& dir){\n #ifndef _WIN32\n DIR* d=opendir(dir.c_str()); if(!d) return;\n struct dirent* e;\n while((e=readdir(d))!=nullptr){\n std::string nm=e->d_name; if(nm=="."||nm=="..") continue;\n std::string path=dir+"/"+nm;\n if(e->d_type==DT_DIR){ scan(path); continue; }\n if(nm.size()>4){std::string ext=nm.substr(nm.size()-4);\n if(ext==".ttf"||ext==".otf"||ext==".TTF"||ext==".OTF") fonts.push_back(nm);}\n } closedir(d);\n #else\n WIN32_FIND_DATAA fd; HANDLE h2=FindFirstFileA((dir+"\\\\*").c_str(),&fd);\n if(h2==INVALID_HANDLE_VALUE) return;\n do { std::string nm=fd.cFileName; if(nm=="."||nm=="..") continue;\n if(fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY){scan(dir+"\\\\"+nm);continue;}\n if(nm.size()>4){std::string ext=nm.substr(nm.size()-4);\n if(ext==".ttf"||ext==".otf"||ext==".TTF"||ext==".OTF") fonts.push_back(nm);}\n } while(FindNextFileA(h2,&fd)); FindClose(h2);\n #endif\n };\n for(auto& d:dirs) scan(d);\n std::sort(fonts.begin(),fonts.end());\n fonts.erase(std::unique(fonts.begin(),fonts.end()),fonts.end());\n return fonts;\n }\n std::string name;\n float size = 12;\n bool loaded = false;\n\n PFont() = default;\n PFont(const std::string& n, float s) : name(n), size(s), loaded(true) {}\n};\n\n\n// =============================================================================\n// TEXTURE\n// =============================================================================\n\n\n// =============================================================================\n// BUFFERED I/O HELPERS\n// =============================================================================\n\nclass BufferedReader {\n std::ifstream f;\npublic:\n explicit BufferedReader(const std::string& path) : f(path) {}\n bool ready() const { return f.is_open() && f.good(); }\n std::string readLine() { std::string l; std::getline(f,l); return f?l:""; }\n void close() { f.close(); }\n};\n\nclass PrintWriter {\n std::ofstream f;\npublic:\n explicit PrintWriter(const std::string& path) : f(path) {}\n template<typename T> void print(const T& v) { f << v; }\n template<typename T> void println(const T& v) { f << v << "\\n"; }\n void println() { f << "\\n"; }\n void flush() { f.flush(); }\n void close() { f.close(); }\n};\n\n\ninline std::ifstream* createInput(const std::string& path) { return new std::ifstream(path,std::ios::binary); }\ninline std::ofstream* createOutput(const std::string& path) { return new std::ofstream(path,std::ios::binary); }\ninline bool saveStream(const std::string& path, const std::vector<unsigned char>& data) { return saveBytes(path,data); }\ninline void launch(const std::string& path) { system(path.c_str()); }\n\n// Stubs (record/raw are not yet implemented)\ninline void beginRecord(const std::string&, const std::string&) {}\ninline void endRecord() {}\ninline void beginRaw(const std::string&, const std::string&) {}\ninline void endRaw() {}\n\n// =============================================================================\n// PSHADER -- GLSL shader wrapper\n// =============================================================================\n\nclass PShader {\npublic:\n GLuint program = 0, vert = 0, frag = 0;\n std::string vertSrc, fragSrc;\n bool linked = false;\n\n PShader() = default;\n PShader(const std::string& v, const std::string& f) : vertSrc(v), fragSrc(f) {}\n\n static GLuint compileShader(GLenum type, const std::string& src) {\n GLuint s = glCreateShader(type);\n const char* c = src.c_str();\n glShaderSource(s, 1, &c, nullptr);\n glCompileShader(s);\n GLint ok; glGetShaderiv(s, GL_COMPILE_STATUS, &ok);\n if (!ok) { char log[512]; glGetShaderInfoLog(s,512,nullptr,log); std::cerr<<"Shader error: "<<log<<"\\n"; }\n return s;\n }\n\n void compile() {\n vert = compileShader(GL_VERTEX_SHADER, vertSrc);\n frag = compileShader(GL_FRAGMENT_SHADER, fragSrc);\n program = glCreateProgram();\n glAttachShader(program,vert); glAttachShader(program,frag);\n glLinkProgram(program);\n GLint ok; glGetProgramiv(program,GL_LINK_STATUS,&ok);\n if (!ok) { char log[512]; glGetProgramInfoLog(program,512,nullptr,log); std::cerr<<"Link error: "<<log<<"\\n"; }\n linked = ok;\n }\n\n void bind() { if (linked) glUseProgram(program); }\n void unbind() { glUseProgram(0); }\n\n void set(const std::string& n, float v) { glUniform1f(glGetUniformLocation(program,n.c_str()),v); }\n void set(const std::string& n, int v) { glUniform1i(glGetUniformLocation(program,n.c_str()),v); }\n void set(const std::string& n, float x, float y) { glUniform2f(glGetUniformLocation(program,n.c_str()),x,y); }\n void set(const std::string& n, float x, float y, float z) { glUniform3f(glGetUniformLocation(program,n.c_str()),x,y,z); }\n void set(const std::string& n, float x, float y, float z, float w){ glUniform4f(glGetUniformLocation(program,n.c_str()),x,y,z,w); }\n\n ~PShader() { if(program)glDeleteProgram(program); if(vert)glDeleteShader(vert); if(frag)glDeleteShader(frag); }\n PShader(const PShader&) __attribute__((error(\n "E0003: PShader value-style copying is not supported. "\n "Declare PShader* instead of PShader. "\n "See " PROCESSING_WEBSITE_URL "/error/E0003.html"\n )));\n PShader& operator=(const PShader&) __attribute__((error(\n "E0003: PShader value-style assignment is not supported. "\n "Declare PShader* instead of PShader. "\n "See " PROCESSING_WEBSITE_URL "/error/E0003.html"\n )));\n PShader(PShader&& o) noexcept\n : program(o.program),vert(o.vert),frag(o.frag),\n vertSrc(o.vertSrc),fragSrc(o.fragSrc),linked(o.linked)\n { o.program=o.vert=o.frag=0; }\n};\n\n\n// =============================================================================\n// GENERIC ARRAYLIST / HASHMAP -- templated Java-style collections\n// =============================================================================\n\n\n\ntemplate<typename K, typename V>\nclass HashMap {\npublic:\n std::map<K,V> data;\n\n void put(const K& k, const V& v) { data[k]=v; }\n V& get(const K& k) { return data[k]; }\n bool containsKey(const K& k) const { return data.count(k)>0; }\n bool containsValue(const V& v) const { for(auto& p:data) if(p.second==v) return true; return false; }\n void remove(const K& k) { data.erase(k); }\n int size() const { return (int)data.size(); }\n bool isEmpty() const { return data.empty(); }\n void clear() { data.clear(); }\n std::vector<K> keySet() const { std::vector<K> r; for(auto& p:data) r.push_back(p.first); return r; }\n std::vector<V> values() const { std::vector<V> r; for(auto& p:data) r.push_back(p.second); return r; }\n V& operator[](const K& k) { return data[k]; }\n};\n\n// =============================================================================\n// TABLEROW -- single row accessor for Table iteration\n// =============================================================================\n\nclass TableRow {\npublic:\n std::vector<std::string>* row = nullptr;\n std::vector<std::string>* cols = nullptr;\n\n TableRow() = default;\n TableRow(std::vector<std::string>& r, std::vector<std::string>& c) : row(&r), cols(&c) {}\n\n std::string getString(int i) const { return (row&&i<(int)row->size())?(*row)[i]:""; }\n std::string getString(const std::string& col) const {\n if (!cols) return "";\n for (int i=0;i<(int)cols->size();i++) if((*cols)[i]==col) return getString(i);\n return "";\n }\n int getInt(int i) const { auto s=getString(i); return s.empty()?0:std::stoi(s); }\n int getInt(const std::string& c) const { auto s=getString(c); return s.empty()?0:std::stoi(s); }\n float getFloat(int i) const { auto s=getString(i); return s.empty()?0:std::stof(s); }\n float getFloat(const std::string& c)const{ auto s=getString(c); return s.empty()?0:std::stof(s); }\n void setString(int i, const std::string& v) { if(row&&i<(int)row->size()) (*row)[i]=v; }\n void setInt(int i, int v) { setString(i, std::to_string(v)); }\n void setFloat(int i, float v) { setString(i, std::to_string(v)); }\n};\n\n// =============================================================================\n// PVECTOR HELPER -- matches Processing Java\'s createVector()\n// =============================================================================\n\ninline PVector createVector(float x, float y, float z=0) { return PVector(x, y, z); }\n\n\n// ---------------------------------------------------------------------------\n// Mixed-type templates for geometry and math functions.\n// Handles calls like rect(int,int,float,float), line(float,int,float,int), etc.\n// ---------------------------------------------------------------------------\n#include <type_traits>\n\n// line()\ntemplate<typename A,typename B,typename C,typename D,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\ninline void line(A x1,B y1,C x2,D y2){ _api::line((float)x1,(float)y1,(float)x2,(float)y2); }\ntemplate<typename A,typename B,typename C,typename D,typename E,typename F,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>&&std::is_arithmetic_v<E>&&std::is_arithmetic_v<F>>>\ninline void line(A x1,B y1,C z1,D x2,E y2,F z2){ _api::line((float)x1,(float)y1,(float)z1,(float)x2,(float)y2,(float)z2); }\n\n// rect()\ntemplate<typename A,typename B,typename C,typename D,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\ninline void rect(A x,B y,C w,D h2){ _api::rect((float)x,(float)y,(float)w,(float)h2); }\ntemplate<typename A,typename B,typename C,typename D,typename E,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>&&std::is_arithmetic_v<E>>>\ninline void rect(A x,B y,C w,D h2,E r){ _api::rect((float)x,(float)y,(float)w,(float)h2,(float)r); }\n\n// ellipse() / circle()\ntemplate<typename A,typename B,typename C,typename D,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\ninline void ellipse(A x,B y,C w,D h2){ _api::ellipse((float)x,(float)y,(float)w,(float)h2); }\ntemplate<typename A,typename B,typename C,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\ninline void circle(A x,B y,C d){ _api::circle((float)x,(float)y,(float)d); }\n\n// point()\ntemplate<typename A,typename B,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\ninline void point(A x,B y){ _api::point((float)x,(float)y); }\ntemplate<typename A,typename B,typename C,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\ninline void point(A x,B y,C z){ _api::point((float)x,(float)y,(float)z); }\n\n// triangle()\ntemplate<typename A,typename B,typename C,typename D,typename E,typename F,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>&&std::is_arithmetic_v<E>&&std::is_arithmetic_v<F>>>\ninline void triangle(A x1,B y1,C x2,D y2,E x3,F y3){ _api::triangle((float)x1,(float)y1,(float)x2,(float)y2,(float)x3,(float)y3); }\n\n// quad()\ntemplate<typename A,typename B,typename C,typename D,typename E,typename F,typename G,typename H,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>&&std::is_arithmetic_v<E>&&std::is_arithmetic_v<F>&&std::is_arithmetic_v<G>&&std::is_arithmetic_v<H>>>\ninline void quad(A x1,B y1,C x2,D y2,E x3,F y3,G x4,H y4){ _api::quad((float)x1,(float)y1,(float)x2,(float)y2,(float)x3,(float)y3,(float)x4,(float)y4); }\n\n// arc()\ntemplate<typename A,typename B,typename C,typename D,typename E,typename F,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>&&std::is_arithmetic_v<E>&&std::is_arithmetic_v<F>>>\ninline void arc(A x,B y,C w,D h2,E start,F stop){ _api::arc((float)x,(float)y,(float)w,(float)h2,(float)start,(float)stop); }\ntemplate<typename A,typename B,typename C,typename D,typename E,typename F,typename G,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>&&std::is_arithmetic_v<E>&&std::is_arithmetic_v<F>&&std::is_arithmetic_v<G>>>\ninline void arc(A x,B y,C w,D h2,E start,F stop,G mode){ _api::arc((float)x,(float)y,(float)w,(float)h2,(float)start,(float)stop,(int)mode); }\n\n// translate() / rotate() / scale()\ntemplate<typename A,typename B,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\ninline void translate(A x,B y){ _api::translate((float)x,(float)y); }\ntemplate<typename A,typename B,typename C,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\ninline void translate(A x,B y,C z){ _api::translate((float)x,(float)y,(float)z); }\ntemplate<typename A,\n typename=std::enable_if_t<std::is_arithmetic_v<A>>>\ninline void rotate(A a){ _api::rotate((float)a); }\ntemplate<typename A,typename B,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\ninline void scale(A s1,B s2){ _api::scale((float)s1,(float)s2); }\n\n// vertex()\ntemplate<typename A,typename B,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\ninline void vertex(A x,B y){ _api::vertex((float)x,(float)y); }\ntemplate<typename A,typename B,typename C,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\ninline void vertex(A x,B y,C z){ _api::vertex((float)x,(float)y,(float)z); }\ntemplate<typename A,typename B,typename C,typename D,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\ninline void vertex(A x,B y,C u,D v2){ _api::vertex((float)x,(float)y,(float)u,(float)v2); }\n\n// text() position\ntemplate<typename A,typename B,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\ninline void text(const std::string& s,A x,B y){ text(s,(float)x,(float)y); }\ntemplate<typename A,typename B,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\ninline void text(const char* s,A x,B y){ text(std::string(s),(float)x,(float)y); }\n\n// text with bounding box -- mixed arithmetic types\ntemplate<typename A,typename B,typename C,typename D,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\ninline void text(const std::string& s,A x,B y,C w,D h2){ text(s,(float)x,(float)y,(float)w,(float)h2); }\ntemplate<typename A,typename B,typename C,typename D,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\ninline void text(const char* s,A x,B y,C w,D h2){ text(std::string(s),(float)x,(float)y,(float)w,(float)h2); }template<typename V,typename A,typename B,\n typename=std::enable_if_t<std::is_arithmetic_v<V>&&std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\ninline void text(V val,A x,B y){ text((float)val,(float)x,(float)y); }\n// char overload -- display as character not number\ntemplate<typename A,typename B>\ninline void text(char c,A x,B y){ text(std::string(1,c),(float)x,(float)y); }\ntemplate<typename A,typename B,typename C,typename D>\ninline void text(char c,A x,B y,C w,D h2){ text(std::string(1,c),(float)x,(float)y,(float)w,(float)h2); }\n\n// map() -- extremely common source of ambiguity\ntemplate<typename V,typename A,typename B,typename C,typename D,\n typename=std::enable_if_t<std::is_arithmetic_v<V>&&std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\ninline float map(V value,A start1,B stop1,C start2,D stop2){\n return _api::map((float)value,(float)start1,(float)stop1,(float)start2,(float)stop2);\n}\n\n// constrain()\ntemplate<typename V,typename A,typename B,\n typename=std::enable_if_t<std::is_arithmetic_v<V>&&std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\ninline float constrain(V val,A lo,B hi){ return _api::constrain((float)val,(float)lo,(float)hi); }\n\n// lerp()\ntemplate<typename A,typename B,typename C,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\ninline float lerp(A a,B b2,C t){ return _api::lerp((float)a,(float)b2,(float)t); }\n\n// bezier() -- 8 arithmetic params\ntemplate<typename A,typename B,typename C,typename D,\n typename E,typename F,typename G,typename H,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&\n std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>&&\n std::is_arithmetic_v<E>&&std::is_arithmetic_v<F>&&\n std::is_arithmetic_v<G>&&std::is_arithmetic_v<H>>>\ninline void bezier(A x1,B y1,C cx1,D cy1,E cx2,F cy2,G x2,H y2){\n bezier((float)x1,(float)y1,(float)cx1,(float)cy1,\n (float)cx2,(float)cy2,(float)x2,(float)y2);\n}\n\n// bezierPoint / bezierTangent / curvePoint / curveTangent -- mixed types\ntemplate<typename A,typename B,typename C,typename D,typename T,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&\n std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>&&\n std::is_arithmetic_v<T>>>\ninline float bezierPoint(A a,B b,C c,D d,T t){\n return bezierPoint((float)a,(float)b,(float)c,(float)d,(float)t);\n}\ntemplate<typename A,typename B,typename C,typename D,typename T,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&\n std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>&&\n std::is_arithmetic_v<T>>>\ninline float bezierTangent(A a,B b,C c,D d,T t){\n return bezierTangent((float)a,(float)b,(float)c,(float)d,(float)t);\n}\n\n// curve() -- 8 params\ntemplate<typename A,typename B,typename C,typename D,\n typename E,typename F,typename G,typename H,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&\n std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>&&\n std::is_arithmetic_v<E>&&std::is_arithmetic_v<F>&&\n std::is_arithmetic_v<G>&&std::is_arithmetic_v<H>>>\ninline void curve(A x0,B y0,C x1,D y1,E x2,F y2,G x3,H y3){\n curve((float)x0,(float)y0,(float)x1,(float)y1,\n (float)x2,(float)y2,(float)x3,(float)y3);\n}\n\n// dist()\n// NOTE: bodies compute directly with std::sqrt rather than delegating to\n// dist((float)x,...) -- the cast-to-float form re-matched this same template\n// (no non-template dist(float,float,float,float) exists at global scope),\n// causing infinite recursion / stack-overflow segfault when called from a\n// non-PApplet class (confirmed via compile-and-run: segfault in Ground\'s\n// constructor in NonOrthogonalCollisionGroundSegments.pde).\n// PApplet::dist is incomplete at this point in the header so can\'t be used\n// here; inlining the computation directly is the cleanest fix.\ntemplate<typename A,typename B,typename C,typename D,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\ninline float dist(A x1,B y1,C x2,D y2){ float dx=(float)x2-(float)x1,dy=(float)y2-(float)y1; return std::sqrt(dx*dx+dy*dy); }\ntemplate<typename A,typename B,typename C,typename D,typename E,typename F,\n typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>&&std::is_arithmetic_v<E>&&std::is_arithmetic_v<F>>>\ninline float dist(A x1,B y1,C z1,D x2,E y2,F z2){ float dx=(float)x2-(float)x1,dy=(float)y2-(float)y1,dz=(float)z2-(float)z1; return std::sqrt(dx*dx+dy*dy+dz*dz); }\n\n// image() -- mixed types, value and pointer variants\n// image() -- draw a PImage to screen\n// All user-facing overloads below; implementation in Processing.cpp\n\n// =============================================================================\n// PAPPLET -- base class for user sketches\n//\n// All Processing state (mouseX, width, frameCount, ...) lives as member fields.\n// All API functions (background, ellipse, fill, ...) are member methods.\n// Processing.cpp defines PApplet::<method>(...) bodies.\n//\n// Usage:\n// struct Sketch : public PApplet {\n// bool firstMousePress = false;\n// void setup() override { size(640,360); }\n// void draw() override { background(0); }\n// void mousePressed() override { firstMousePress = true; }\n// };\n// int main() { Sketch s; s.run(); return 0; }\n// =============================================================================\n\nstruct PApplet {\n // ── Public state (directly accessible in sketch code) ───────────────────\n int winWidth = 640, winHeight = 480;\n int logicalW = 640, logicalH = 480;\n int displayWidth = 0, displayHeight = 0;\n int pixelWidth = 0, pixelHeight = 0;\n int pixelDensityValue = 1;\n bool isResizable = false;\n bool focused = false;\n\n // width/height: direct accessors (no reference members — they break copy ctor)\n int& width = logicalW;\n int& height = logicalH;\n\n float mouseX = 0, mouseY = 0, pmouseX = 0, pmouseY = 0;\n float mouseDX = 0, mouseDY = 0;\n bool _mousePressed = false;\n int mouseButton = -1;\n\n bool _keyPressed = false;\n int keyCode = 0;\n // char16_t, not char -- Java\'s "char" is genuinely a 16-bit type\n // (a UTF-16 code unit), wide enough to hold CODED\'s real value\n // (0xFFFF) without truncation. A C++ "char" is only 8 bits, so\n // "(char)CODED" always truncated down to 0xFF regardless of what\n // CODED\'s declared value was. char16_t is the real, character-\n // semantic C++ type for exactly this situation -- str()/String\n // concatenation get dedicated overloads (see below) so key still\n // displays/concatenates as a character, matching Java\'s actual\n // behavior, rather than falling back to int\'s numeric formatting.\n char16_t key = 0;\n bool keys[349] = {}; // all currently held GLFW keycodes (AAA-style flat array)\n bool mouseButtons[8] = {}; // all currently held mouse buttons (GLFW_MOUSE_BUTTON_*)\n bool keysDown[256] = {}; // Processing-keycode-indexed, mirrors upper/lower letters\n bool mouseDown[128] = {}; // Processing mouse-button-indexed (LEFT/RIGHT/CENTER)\n bool _eventDrewSomething = false; // set true if keyPressed()/mousePressed() drew\n bool _backgroundCalledThisFrame = false; // set true if background() was called this frame\n\n int frameCount = 1;\n float currentFrameRate = 60.0f;\n float _frameRate = 60.0f;\n float deltaTime = 0.0f;\n bool looping = true;\n float measuredFrameRate = 0.0f;\n\n float fillR = 1, fillG = 1, fillB = 1, fillA = 1;\n float strokeR = 0, strokeG = 0, strokeB = 0, strokeA = 1;\n float strokeW = 1;\n bool doFill = true, doStroke = true, smoothing = true;\n\n int currentRectMode = CORNER;\n int currentEllipseMode = CENTER;\n int currentImageMode = CORNER;\n\n float tintR = 1, tintG = 1, tintB = 1, tintA = 1;\n bool doTint = false;\n\n int colorModeVal = RGB;\n float colorMaxH = 255.f, colorMaxS = 255.f, colorMaxB = 255.f, colorMaxA = 255.f;\n\n std::vector<unsigned int> pixels;\n\n std::string g_sketchName = "Sketch";\n\n // Event callback function pointers (wired by PApplet::run())\n std::function<void()> _onKeyPressed;\n std::function<void()> _onKeyReleased;\n std::function<void()> _onKeyTyped;\n std::function<void()> _onMousePressed;\n std::function<void()> _onMouseReleased;\n std::function<void()> _onMouseClicked;\n std::function<void()> _onMouseMoved;\n std::function<void()> _onMouseDragged;\n std::function<void(int)> _onMouseWheel;\n std::function<void()> _onWindowMoved;\n std::function<void()> _onWindowResized;\n\n void (*_wireCallbacksFn)() = nullptr;\n void (*_staticSketchSetup)() = nullptr;\n\n // ── Lifecycle (user overrides these) ────────────────────────────────────\n virtual void setup() {}\n virtual void draw() {}\n virtual void settings() {}\n virtual void mousePressed() {}\n virtual void mouseReleased() {}\n virtual void mouseClicked() {}\n virtual void mouseMoved() {}\n virtual void mouseDragged() {}\n virtual void mouseWheel(int delta) {}\n virtual void keyPressed() {}\n virtual void keyReleased() {}\n virtual void keyTyped() {}\n virtual void windowMoved() {}\n virtual void windowResized() {}\n\n bool isMousePressed() const { return _mousePressed; }\n bool isKeyPressed() const { return _keyPressed; }\n\n // ── run() ────────────────────────────────────────────────────────────────\n void run();\n\n // ── Static instance pointer ──────────────────────────────────────────────\n static PApplet* g_papplet;\n\n // ── Environment ─────────────────────────────────────────────────────────\n void size(int w, int h);\n void size(int w, int h, int renderer);\n void fullScreen();\n void frameRate(int fps);\n void vsync(bool on); // vsync(true)=cap to display refresh, vsync(false)=uncapped\n void noLoop();\n void loop();\n void redraw();\n void exit_sketch();\n void exit() { exit_sketch(); }\n void windowTitle(const std::string& t);\n void windowMove(int x, int y);\n void windowResize(int w, int h);\n void windowResizable(bool r);\n void windowRatio(int w, int h);\n void pixelDensity(int d);\n void smooth(int level = 2); // real Processing\'s P2D/P3D default is smooth(2)\n void noSmooth();\n int smoothLevel = 2; // current level, used by PGraphics to match the main canvas\'s AA quality\n void hint(int which);\n void cursor();\n void cursor(int type);\n void noCursor();\n void captureMouse();\n void releaseMouse();\n void setTitle(const std::string& t) { windowTitle(t); }\n void setLocation(int x, int y) { windowMove(x,y); }\n void setResizable(bool r) { windowResizable(r); }\n void setClipboard(const std::string& s);\n std::string getClipboard();\n void setWindowIcon(PImage* img);\n bool isCtrlDown();\n bool isShiftDown();\n bool isAltDown();\n int displayDensity() { return pixelDensityValue; }\n void setProjection(int w, int h); // also called from resize callback\n void enableDebugConsole();\n\n static unsigned long millis() {\n static auto _start = std::chrono::steady_clock::now();\n return (unsigned long)std::chrono::duration_cast<std::chrono::milliseconds>(\n std::chrono::steady_clock::now() - _start).count();\n }\n static int second() { std::time_t t=std::time(nullptr); return std::localtime(&t)->tm_sec; }\n static int minute() { std::time_t t=std::time(nullptr); return std::localtime(&t)->tm_min; }\n static int hour() { std::time_t t=std::time(nullptr); return std::localtime(&t)->tm_hour; }\n static int day() { std::time_t t=std::time(nullptr); return std::localtime(&t)->tm_mday; }\n static int month() { std::time_t t=std::time(nullptr); return std::localtime(&t)->tm_mon+1; }\n static int year() { std::time_t t=std::time(nullptr); return std::localtime(&t)->tm_year+1900;}\n static void delay(int ms) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); }\n void thread(std::function<void()> fn) { std::thread(fn).detach(); }\n\n // ── Style stack ──────────────────────────────────────────────────────────\n void push();\n void pop();\n void pushStyle();\n void popStyle();\n void pushMatrix();\n void popMatrix();\n\n // ── Color mode ───────────────────────────────────────────────────────────\n void colorMode(int mode, float mx=255.f);\n void colorMode(int mode, float mH, float mS, float mB, float mA=255.f);\n color makeColor(float a, float b, float c, float d=255);\n color makeColor(float gray, float alpha=255);\n float red(color c);\n float green(color c);\n float blue(color c);\n float alpha(color c);\n float brightness(color c);\n float saturation(color c);\n float hue(color c);\n color lerpColor(color c1, color c2, float t);\n\n // ── Background / clear ───────────────────────────────────────────────────\n void background(float gray, float a=255.f);\n void background(float r, float g, float b, float a=255.f);\n void background(color c);\n void background(const PImage& img);\n void background(const PImage* img) { if(img) background(*img); }\n void background(const PColor& c);\n template<typename A, typename=std::enable_if_t<std::is_arithmetic_v<A>>>\n void background(A gray) { background((float)gray); }\n template<typename A, typename B, typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\n void background(A gray, B a) { background((float)gray,(float)a); }\n template<typename A, typename B, typename C, typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\n void background(A r, B g, C b) { background((float)r,(float)g,(float)b); }\n template<typename A, typename B, typename C, typename D, typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\n void background(A r, B g, C b, D a) { background((float)r,(float)g,(float)b,(float)a); }\n void clear();\n\n // ── Fill ─────────────────────────────────────────────────────────────────\n void fill(float gray, float a);\n void fill(float gray);\n void fill(float r, float g, float b, float a);\n void fill(float r, float g, float b);\n void fill(color c);\n void fill(color c, float a);\n void fill(color c, int a) { fill(c,(float)a); }\n void fill(const PColor& c);\n void noFill();\n template<typename A, typename=std::enable_if_t<std::is_arithmetic_v<A>>>\n void fill(A gray) { fill((float)gray); }\n template<typename A, typename B, typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\n void fill(A gray, B a) { fill((float)gray,(float)a); }\n template<typename A, typename B, typename C, typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\n void fill(A r, B g, C b) { fill((float)r,(float)g,(float)b); }\n template<typename A, typename B, typename C, typename D, typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\n void fill(A r, B g, C b, D a) { fill((float)r,(float)g,(float)b,(float)a); }\n\n // ── Stroke ───────────────────────────────────────────────────────────────\n void stroke(float gray, float a);\n void stroke(float gray);\n void stroke(float r, float g, float b, float a);\n void stroke(float r, float g, float b);\n void stroke(color c);\n void stroke(const PColor& c);\n void noStroke();\n void strokeWeight(float w);\n void strokeCap(int cap);\n void strokeJoin(int join);\n template<typename A, typename=std::enable_if_t<std::is_arithmetic_v<A>>>\n void stroke(A gray) { stroke((float)gray); }\n template<typename A, typename B, typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\n void stroke(A gray, B a) { stroke((float)gray,(float)a); }\n template<typename A, typename B, typename C, typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\n void stroke(A r, B g, C b) { stroke((float)r,(float)g,(float)b); }\n template<typename A, typename B, typename C, typename D, typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\n void stroke(A r, B g, C b, D a) { stroke((float)r,(float)g,(float)b,(float)a); }\n template<typename A, typename=std::enable_if_t<std::is_arithmetic_v<A>>>\n void strokeWeight(A w) { strokeWeight((float)w); }\n\n // ── Tint ─────────────────────────────────────────────────────────────────\n void tint(float gray, float a);\n void tint(float gray);\n void tint(float r, float g, float b, float a);\n void tint(float r, float g, float b);\n void tint(const PColor& c);\n void noTint();\n template<typename A, typename=std::enable_if_t<std::is_arithmetic_v<A>>>\n void tint(A gray) { tint((float)gray); }\n template<typename A, typename B, typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\n void tint(A gray, B a) { tint((float)gray,(float)a); }\n template<typename A, typename B, typename C, typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\n void tint(A r, B g, C b) { tint((float)r,(float)g,(float)b); }\n template<typename A, typename B, typename C, typename D, typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\n void tint(A r, B g, C b, D a) { tint((float)r,(float)g,(float)b,(float)a); }\n\n // ── Shape attributes ─────────────────────────────────────────────────────\n void rectMode(int mode);\n void ellipseMode(int mode);\n\n // ── 2D primitives ────────────────────────────────────────────────────────\n void point(float x, float y);\n void point(float x, float y, float z);\n void line(float x1, float y1, float x2, float y2);\n void line(float x1, float y1, float z1, float x2, float y2, float z2);\n void ellipse(float cx, float cy, float w, float h);\n void circle(float cx, float cy, float d);\n void rect(float x, float y, float w, float h);\n void rect(float x, float y, float w, float h, float r);\n void square(float x, float y, float s);\n void triangle(float x1, float y1, float x2, float y2, float x3, float y3);\n void quad(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4);\n void arc(float cx, float cy, float w, float h, float start, float stop);\n void arc(float cx, float cy, float w, float h, float start, float stop, int mode);\n template<typename A,typename B,typename C,typename D,typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\n void ellipse(A cx,B cy,C w,D h){ ellipse((float)cx,(float)cy,(float)w,(float)h); }\n template<typename A,typename B,typename C,typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\n void circle(A cx,B cy,C d){ circle((float)cx,(float)cy,(float)d); }\n template<typename A,typename B,typename C,typename D,typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\n void rect(A x,B y,C w,D h){ rect((float)x,(float)y,(float)w,(float)h); }\n template<typename A,typename B,typename C,typename D,typename E,typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>&&std::is_arithmetic_v<E>>>\n void rect(A x,B y,C w,D h,E r){ rect((float)x,(float)y,(float)w,(float)h,(float)r); }\n template<typename A,typename B,typename C,typename D,typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\n void line(A x1,B y1,C x2,D y2){ line((float)x1,(float)y1,(float)x2,(float)y2); }\n template<typename A,typename B,typename C,typename D,typename E,typename F,typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>&&std::is_arithmetic_v<E>&&std::is_arithmetic_v<F>>>\n void line(A x1,B y1,C z1,D x2,E y2,F z2){ line((float)x1,(float)y1,(float)z1,(float)x2,(float)y2,(float)z2); }\n template<typename A,typename B,typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\n void point(A x,B y){ point((float)x,(float)y); }\n\n // ── 3D primitives ────────────────────────────────────────────────────────\n void box(float size);\n void box(float w, float h, float d);\n void sphere(float r);\n void sphereDetail(int res);\n void rotateX(float angle);\n void rotateY(float angle);\n void rotateZ(float angle);\n template<typename A,typename=std::enable_if_t<std::is_arithmetic_v<A>>>\n void rotateX(A a){ rotateX((float)a); }\n template<typename A,typename=std::enable_if_t<std::is_arithmetic_v<A>>>\n void rotateY(A a){ rotateY((float)a); }\n template<typename A,typename=std::enable_if_t<std::is_arithmetic_v<A>>>\n void rotateZ(A a){ rotateZ((float)a); }\n\n // ── Vertex / shapes ──────────────────────────────────────────────────────\n void beginShape(int kind=-1);\n void endShape(int mode=0);\n void vertex(float x, float y);\n void vertex(float x, float y, float z);\n void vertex(float x, float y, float u, float v);\n void vertex(float x, float y, float z, float u, float v);\n void bezierVertex(float cx1, float cy1, float cx2, float cy2, float x, float y);\n void quadraticVertex(float cx, float cy, float x, float y);\n void curveVertex(float x, float y);\n void beginContour();\n void endContour();\n void bezier(float x1,float y1,float cx1,float cy1,float cx2,float cy2,float x2,float y2);\n void curve(float x0,float y0,float x1,float y1,float x2,float y2,float x3,float y3);\n float bezierPoint(float a, float b, float c, float d, float t);\n float bezierTangent(float a, float b, float c, float d, float t);\n float curvePoint(float a, float b, float c, float d, float t);\n float curveTangent(float a, float b, float c, float d, float t);\n void curveDetail(int d);\n void curveTightness(float t);\n void bezierDetail(int d);\n\n // ── Matrix ───────────────────────────────────────────────────────────────\n void resetMatrix();\n void applyMatrix(float n00,float n01,float n02,float n03,\n float n10,float n11,float n12,float n13,\n float n20,float n21,float n22,float n23,\n float n30,float n31,float n32,float n33);\n void translate(float x, float y);\n void translate(float x, float y, float z);\n void scale(float s);\n void scale(float sx, float sy);\n void rotate(float angle);\n void shearX(float angle);\n void shearY(float angle);\n void printMatrix();\n float screenX(float x, float y, float z=0);\n float screenY(float x, float y, float z=0);\n float screenZ(float x, float y, float z=0);\n float modelX(float x, float y, float z=0);\n float modelY(float x, float y, float z=0);\n float modelZ(float x, float y, float z=0);\n template<typename A,typename B,typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\n void translate(A x,B y){ translate((float)x,(float)y); }\n template<typename A,typename B,typename C,typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>>>\n void translate(A x,B y,C z){ translate((float)x,(float)y,(float)z); }\n template<typename A,typename=std::enable_if_t<std::is_arithmetic_v<A>>>\n void scale(A s){ scale((float)s); }\n template<typename A,typename B,typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\n void scale(A sx,B sy){ scale((float)sx,(float)sy); }\n template<typename A,typename=std::enable_if_t<std::is_arithmetic_v<A>>>\n void rotate(A a){ rotate((float)a); }\n template<typename A,typename B,typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\n void size(A w,B h){ size((int)w,(int)h); }\n template<typename A,typename B,typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>>>\n void size(A w,B h,int renderer){ size((int)w,(int)h,renderer); }\n\n // ── Camera ───────────────────────────────────────────────────────────────\n void camera();\n void camera(float ex,float ey,float ez,float cx,float cy,float cz,float ux,float uy,float uz);\n void beginCamera();\n void endCamera();\n void perspective();\n void perspective(float fov, float aspect, float zNear, float zFar);\n void ortho();\n void ortho(float l, float r, float b, float t, float n, float f);\n void frustum(float l, float r, float b, float t, float n, float f);\n void printCamera();\n void printProjection();\n\n // ── Lights ───────────────────────────────────────────────────────────────\n void lights();\n void noLights();\n void ambientLight(float r, float g, float b);\n void ambientLight(float r, float g, float b, float x, float y, float z);\n void directionalLight(float r, float g, float b, float nx, float ny, float nz);\n void pointLight(float r, float g, float b, float x, float y, float z);\n void spotLight(float r, float g, float b, float x, float y, float z,\n float nx, float ny, float nz, float angle, float conc);\n void lightFalloff(float c, float l, float q);\n void lightSpecular(float r, float g, float b);\n void normal(float nx, float ny, float nz);\n\n // ── Material ─────────────────────────────────────────────────────────────\n void ambient(float r, float g, float b);\n void ambient(color c);\n void emissive(float r, float g, float b);\n void emissive(color c);\n void specular(float r, float g, float b);\n void specular(color c);\n void shininess(float s);\n\n // ── Text ─────────────────────────────────────────────────────────────────\n void text(const std::string& msg, float x, float y);\n void text(const std::string& msg, float x, float y, float w, float h);\n void text(int val, float x, float y);\n void text(float val, float x, float y);\n void text(char c, float x, float y) { text(std::string(1,c), x, y); }\n void text(char c, float x, float y, float w, float h) { text(std::string(1,c), x, y, w, h); }\n template<typename X,typename Y,typename=std::enable_if_t<std::is_arithmetic_v<X>&&std::is_arithmetic_v<Y>>>\n void text(char c, X x, Y y) { text(std::string(1,c),(float)x,(float)y); }\n void textSize(float size);\n void textAlign(int alignX, int alignY=-1);\n void textLeading(float leading);\n void textMode(int mode);\n float textWidth(const std::string& s);\n float textAscent();\n float textDescent();\n PFont loadFont(const std::string& filename);\n PFont* createFont(const std::string& name, float size, bool smooth=true);\n void textFont(const PFont& font);\n void textFont(const PFont& font, float size);\n void textFont(const PFont* font) { if(font) textFont(*font); }\n void textFont(const PFont* font, float size) { if(font) textFont(*font,size); }\n template<typename X,typename Y,typename=std::enable_if_t<std::is_arithmetic_v<X>&&std::is_arithmetic_v<Y>>>\n void text(const std::string& s,X x,Y y){ text(s,(float)x,(float)y); }\n template<typename X,typename Y,typename=std::enable_if_t<std::is_arithmetic_v<X>&&std::is_arithmetic_v<Y>>>\n void text(int v,X x,Y y){ text(v,(float)x,(float)y); }\n template<typename X,typename Y,typename=std::enable_if_t<std::is_arithmetic_v<X>&&std::is_arithmetic_v<Y>>>\n void text(float v,X x,Y y){ text(v,(float)x,(float)y); }\n\n // ── Image ────────────────────────────────────────────────────────────────\n PImage* loadImage(const std::string& path);\n PImage* createImage(int w, int h, int mode=1);\n PGraphics* createGraphics(int w, int h);\n PGraphics* createGraphics(int w, int h, int renderer); // matches size(w,h,renderer)\n PImage* requestImage(const std::string& path);\n void imageMode(int mode);\n void image(PImage* img, float x, float y);\n void image(PImage* img, float x, float y, float w, float h);\n void image(const PImage& img, float x, float y);\n void image(const PImage& img, float x, float y, float w, float h);\n void image(const PImage* img, float x, float y) { if(img) image(*img,x,y); }\n void image(const PImage* img, float x, float y, float w, float h) { if(img) image(*img,x,y,w,h); }\n void image(PGraphics& pg, float x, float y);\n void image(PGraphics& pg, float x, float y, float w, float h);\n // BUG FIX: sketches declare "PGraphics* pg;" (today\'s convention --\n // PGraphics is non-copyable, so it\'s always pointer-typed), and call\n // "image(pg, x, y);" with that pointer directly. Without an explicit\n // PGraphics* overload, C++ overload resolution silently converts\n // PGraphics* to PImage* (since PGraphics publicly inherits PImage)\n // and calls the PLAIN PImage* image() overload instead -- completely\n // bypassing drawPGraphicsRect() and its FBO-texture-aware rendering.\n // The buffer\'s content was always being rendered correctly\n // internally; it just never reached the screen, because the wrong\n // overload silently won via an implicit derived-to-base pointer\n // conversion that nothing here ever warned about.\n void image(PGraphics* pg, float x, float y) { if (pg) image(*pg, x, y); }\n void image(PGraphics* pg, float x, float y, float w, float h) { if (pg) image(*pg, x, y, w, h); }\n void filter(int mode);\n void filter(int mode, float param);\n void loadPixels();\n void updatePixels();\n color get(int x, int y);\n void set(int x, int y, color c);\n void saveFrame(const std::string& filename="frame-####.png");\n void save(const std::string& filename);\n\n // ── Blend / clip ─────────────────────────────────────────────────────────\n void blendMode(int mode);\n void clip(float x, float y, float w, float h);\n void noClip();\n void blend(int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, int mode);\n void copy(int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh);\n\n // ── Texture ──────────────────────────────────────────────────────────────\n void textureMode(int mode);\n void textureWrap(int mode);\n void texture(PImage& img);\n\n // ── Shader ───────────────────────────────────────────────────────────────\n PShader* loadShader(const std::string& fragPath, const std::string& vertPath="");\n void shader(PShader& s);\n void resetShader();\n\n // ── PShape ───────────────────────────────────────────────────────────────\n PShape createShape(int kind=-1);\n PShape* loadShape(const std::string& path);\n void shape(const PShape& s, float x=0, float y=0);\n void shape(const PShape& s, float x, float y, float w, float h);\n void shape(const PShape* s, float x=0, float y=0) { if(s) shape(*s,x,y); }\n void shape(const PShape* s, float x, float y, float w, float h) { if(s) shape(*s,x,y,w,h); }\n void shapeMode(int mode);\n\n // ── Math ─────────────────────────────────────────────────────────────────\n static float sin(float x) { return std::sin(x); }\n static float cos(float x) { return std::cos(x); }\n static float tan(float x) { return std::tan(x); }\n static float asin(float x) { return std::asin(x); }\n static float acos(float x) { return std::acos(x); }\n static float atan(float x) { return std::atan(x); }\n static float atan2(float y, float x) { return std::atan2(y,x); }\n static float sqrt(float x) { return std::sqrt(x); }\n static float sq(float x) { return x*x; }\n static float abs(float x) { return std::fabs(x); }\n static float ceil(float x) { return std::ceil(x); }\n static float floor(float x) { return std::floor(x); }\n static float round(float x) { return std::round(x); }\n static float exp(float x) { return std::exp(x); }\n static float log(float x) { return std::log(x); }\n static float pow(float b, float e) { return std::pow(b,e); }\n static float mag(float x, float y) { return std::sqrt(x*x+y*y); }\n static float mag(float x, float y, float z) { return std::sqrt(x*x+y*y+z*z); }\n static float norm(float v, float lo, float hi) { return (v-lo)/(hi-lo); }\n static float degrees(float r) { return r*180.f/PI; }\n static float radians(float d) { return d*PI/180.f; }\n static float lerp(float a, float b, float t) { return a+t*(b-a); }\n static float dist(float x1,float y1,float x2,float y2)\n { float dx=x2-x1,dy=y2-y1; return std::sqrt(dx*dx+dy*dy); }\n static float dist(float x1,float y1,float z1,float x2,float y2,float z2)\n { float dx=x2-x1,dy=y2-y1,dz=z2-z1; return std::sqrt(dx*dx+dy*dy+dz*dz); }\n static float map(float v,float i0,float i1,float o0,float o1) { return o0+(v-i0)*(o1-o0)/(i1-i0); }\n static float constrain(float v,float lo,float hi) { return v<lo?lo:(v>hi?hi:v); }\n static float max(float a,float b) { return a>b?a:b; }\n static float min(float a,float b) { return a<b?a:b; }\n static float max(float a,float b,float c) { float m=a>b?a:b; return m>c?m:c; }\n static float min(float a,float b,float c) { float m=a<b?a:b; return m<c?m:c; }\n static bool isNaN(float v) { return std::isnan(v); }\n static bool isInfinite(float v) { return std::isinf(v); }\n template<typename A,typename B,typename C,typename D,typename=std::enable_if_t<std::is_arithmetic_v<A>&&std::is_arithmetic_v<B>&&std::is_arithmetic_v<C>&&std::is_arithmetic_v<D>>>\n static float dist(A x1,B y1,C x2,D y2){ float dx=(float)x2-(float)x1,dy=(float)y2-(float)y1; return std::sqrt(dx*dx+dy*dy); }\n\n // ── Random / noise ───────────────────────────────────────────────────────\n void randomSeed(long s);\n float random(float lo, float hi);\n float random(float hi);\n float randomGaussian();\n void noiseSeed(int seed);\n void noiseDetail(int octaves, float falloff=0.5f);\n float noise(float x);\n float noise(float x, float y);\n float noise(float x, float y, float z);\n\n // ── Print ────────────────────────────────────────────────────────────────\n template<typename T> static void print(const T& v) { std::cout << v; std::cout.flush(); }\n template<typename T> static void println(const T& v) { std::cout << v << "\\n"; std::cout.flush(); }\n static void println() { std::cout << "\\n"; std::cout.flush(); }\n\n // ── String helpers ───────────────────────────────────────────────────────\n static std::string str(int v) { return std::to_string(v); }\n static std::string str(float v) { return std::to_string(v); }\n static std::string str(bool v) { return v?"true":"false"; }\n static std::string str(char v) { return std::string(1,v); }\n static std::string str(char16_t v) { return std::string(1,(char)v); }\n static std::vector<std::string> split(const std::string& s, char d);\n static std::vector<std::string> splitTokens(const std::string& s, const std::string& d);\n static std::string join(const std::vector<std::string>& v, const std::string& sep);\n static std::string trim(const std::string& s);\n static std::string nf(float v, int digits);\n static std::string nf(int v, int d);\n static std::string hex(int v);\n static std::string binary(int v);\n static int toInt(const std::string& s) { try{return std::stoi(s);}catch(...){return 0;} }\n static float toFloat(const std::string& s) { try{return std::stof(s);}catch(...){return 0;} }\n static bool toBoolean(const std::string& s) { return s=="true"||s=="1"||s=="yes"; }\n\n // ── File I/O ─────────────────────────────────────────────────────────────\n static std::vector<std::string> loadStrings(const std::string& path);\n static bool saveStrings(const std::string& path, const std::vector<std::string>& lines);\n static std::vector<unsigned char> loadBytes(const std::string& path);\n static bool saveBytes(const std::string& path, const std::vector<unsigned char>& d);\n static BufferedReader* createReader(const std::string& path);\n static PrintWriter* createWriter(const std::string& path);\n static std::string selectInput(const std::string& prompt="", const std::string& filter="");\n static std::string selectOutput(const std::string& prompt="", const std::string& filter="");\n static std::string selectFolder(const std::string& prompt="");\n\n // ── JSON ─────────────────────────────────────────────────────────────────\n static JSONValue parseJSON(const std::string& src);\n static std::string toJSONString(const JSONValue& v, int indent=0);\n static JSONValue loadJSONObject(const std::string& path);\n static JSONValue loadJSONArray(const std::string& path);\n static bool saveJSONObject(const std::string& path, const JSONValue& v, int indent=2);\n static bool saveJSONArray(const std::string& path, const JSONValue& v, int indent=2);\n static JSONValue parseJSONObject(const std::string& s) { return parseJSON(s); }\n static JSONValue parseJSONArray(const std::string& s) { return parseJSON(s); }\n\n // ── XML ──────────────────────────────────────────────────────────────────\n static XML loadXML(const std::string& path);\n static XML parseXML(const std::string& src);\n static bool saveXML(const std::string& path, const XML& x);\n\n // ── Table ────────────────────────────────────────────────────────────────\n static Table* loadTable(const std::string& path, const std::string& options="header");\n static bool saveTable(const std::string& path, const Table& t, const std::string& ext="csv");\n\n // ── No-ops ───────────────────────────────────────────────────────────────\n static void beginRecord(const std::string&, const std::string&) {}\n static void endRecord() {}\n static void beginRaw(const std::string&, const std::string&) {}\n static void endRaw() {}\n\n // ── Private state ────────────────────────────────────────────────────────\n // (implementation details, not for user code)\n GLFWwindow* gWindow = nullptr;\n bool mouseInWindow = false;\n bool is3DMode = false;\n int sphereRes = 48;\n int curveDetailVal = 20;\n float curveTightnessVal = 0.0f;\n int bezierDetailVal = 60;\n bool lightsEnabled = false;\n int lightIndex = 0;\n bool redrawOnce = false;\n double targetFrameTime = 1.0 / 60.0;\n bool defaultP3D = false;\n bool _setupDone = false;\n bool _inWinsizeCb = false;\n bool mouseWasPressed = false;\n bool g_pendingKeyPressed = false;\n int g_currentMods = 0;\n char _g_lastChar = 0;\n\n float pendingSpecR = 0, pendingSpecG = 0, pendingSpecB = 0;\n float lightConcentration[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };\n float lightCutoffCos[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };\n\n int shapeKind = -1;\n bool inShape = false, inContour = false;\n bool shape3D = false;\n std::vector<std::pair<float,float>> shapeVerts;\n std::vector<std::array<float,3>> shapeVerts3D;\n std::vector<std::pair<float,float>> contourVerts;\n\n struct Style {\n float fillR, fillG, fillB, fillA;\n float strokeR, strokeG, strokeB, strokeA, strokeW;\n bool doFill, doStroke;\n int rectMode, ellipseMode, imageMode;\n float tintR, tintG, tintB, tintA;\n bool doTint;\n int colorMode;\n float cmH, cmS, cmB, cmA;\n };\n std::vector<Style> styleStack;\n\n float bgR = 0.8f, bgG = 0.8f, bgB = 0.8f, bgA = 1.0f;\n\n // Noise\n static const int PERLIN_YWRAPB = 4;\n static const int PERLIN_YWRAP = 1 << PERLIN_YWRAPB;\n static const int PERLIN_ZWRAPB = 8;\n static const int PERLIN_ZWRAP = 1 << PERLIN_ZWRAPB;\n static const int PERLIN_SIZE = 4095;\n int noiseOctaves = 4;\n float noiseFalloff = 0.5f;\n float perlinTable[PERLIN_SIZE + 1] = {};\n bool perlinInit = false;\n\n std::mt19937 _rng{std::mt19937::default_seed};\n std::uniform_real_distribution<float> _rngDist{0.0f, 1.0f};\n\n // Font / text\n float g_textSize = 14.0f;\n int g_textAlignX = LEFT_ALIGN;\n int g_textAlignY = BASELINE;\n float g_textLeading = 0.0f;\n#if PROCESSING_HAS_STB_TRUETYPE\n struct TTFAtlas {\n GLuint texID = 0;\n stbtt_bakedchar chars[96];\n };\n struct TTFFont {\n stbtt_fontinfo info;\n std::vector<unsigned char> data;\n std::unordered_map<int, TTFAtlas> atlasCache; // keyed by round(pixelSize*2)\n TTFAtlas* current = nullptr;\n GLuint texID = 0;\n stbtt_bakedchar* chars = nullptr;\n int atlasW = 512, atlasH = 512;\n float bakeSize = 0.0f;\n bool loaded = false;\n };\n TTFFont g_ttf;\n float ttfStrWidth(const std::string& s);\n void drawTTFStr(float x, float y, const std::string& s);\n#endif\n std::vector<PFont> _fontPool;\n PFont currentFont;\n\n // FBO persistence\n GLuint persistFBO = 0, persistTex = 0;\n\n // Phong shader\n GLuint phongProg = 0;\n\n // Texture / shader\n int textureModeVal = IMAGE;\n int textureWrapVal = CLAMP;\n PShader* activeShader = nullptr;\n\n // PShape draw mode\n int shapeDrawMode = CORNER;\n\n // OBJ loader scratch\n std::string objDir;\n\n virtual ~PApplet() = default;\n\nprotected:\n // Internal helpers (defined in Processing.cpp)\n void applyFill();\n void applyStroke();\n void restoreLighting();\n void initPersistFBO();\n void saveToPersist();\n void restoreFromPersist();\n void _restoreMainCanvas();\n void drawEllipseGeom(float cx,float cy,float rx,float ry,float sa=0,float ea=TWO_PI,int segs=-1);\n void resolveRect(float& x,float& y,float& w,float& h);\n void resolveEllipse(float& cx,float& cy,float& rx,float& ry);\n void setFillFromColor(color c);\n void setStrokeFromColor(color c);\n void applyDefaultCamera();\n void applyStandardModelview();\n void captureStyle(Style& s);\n void restoreStyle(const Style& s);\n void initPhongShader();\n void initPerlin(unsigned int seed);\n void bakeAtlas(float pixelSize);\n bool loadTTFFile(const std::string& path);\n bool tryLoadTTF(const std::string& path, float size);\n void drawBitmapStr(float x, float y, const std::string& s, int scale);\n float bitmapStrWidth(const std::string& s, int scale);\n#if PROCESSING_HAS_STB_TRUETYPE\n#endif\n float getLineWidth(const std::string& line);\n void renderText(const std::string& msg, float x, float y);\n void drawImageRect(PImage& img, float x, float y, float w, float h);\n void drawImage_impl(PImage* img, float x, float y, float w, float h);\n void drawPGraphicsRect(PGraphics& pg, float x, float y, float w, float h);\n void drawPShape(const PShape& s, float x, float y, float w=-1, float h=-1, bool parentStyleEnabled=true);\n void hsbToRgb(float h, float s, float b, float& outR, float& outG, float& outB);\n void setBg(float r, float g, float b, float a);\n\n // GLFW callback installers / forwarders\n void installCallbacks();\n\n // Static GLFW callbacks (forward to g_papplet)\n static void cb_cursor_pos(GLFWwindow*, double x, double y);\n static void cb_mouse_btn(GLFWwindow*, int btn, int action, int mods);\n static void cb_scroll(GLFWwindow*, double, double yoffset);\n static void cb_char(GLFWwindow*, unsigned int codepoint);\n static void cb_key(GLFWwindow*, int k, int scancode, int action, int mods);\n static void cb_focus(GLFWwindow*, int f);\n static void cb_winpos(GLFWwindow*, int, int);\n static void cb_winsize(GLFWwindow*, int lw, int lh);\n static void cb_fbsize(GLFWwindow*, int fw, int fh);\n static void cb_winrefresh(GLFWwindow* w);\n static int glfw_to_java_keycode(int k);\n};\n\ninline PApplet* PApplet::g_papplet = nullptr;\n// ── _api:: implementations (after PApplet is complete) ───────────────────────\nnamespace _api {\n inline void line(float x1,float y1,float x2,float y2){ if(PApplet::g_papplet) PApplet::g_papplet->line(x1,y1,x2,y2); }\n inline void line(float x1,float y1,float z1,float x2,float y2,float z2){ if(PApplet::g_papplet) PApplet::g_papplet->line(x1,y1,z1,x2,y2,z2); }\n inline void rect(float x,float y,float w,float h){ if(PApplet::g_papplet) PApplet::g_papplet->rect(x,y,w,h); }\n inline void rect(float x,float y,float w,float h,float r){ if(PApplet::g_papplet) PApplet::g_papplet->rect(x,y,w,h,r); }\n inline void ellipse(float x,float y,float w,float h){ if(PApplet::g_papplet) PApplet::g_papplet->ellipse(x,y,w,h); }\n inline void circle(float x,float y,float d){ if(PApplet::g_papplet) PApplet::g_papplet->circle(x,y,d); }\n inline void point(float x,float y){ if(PApplet::g_papplet) PApplet::g_papplet->point(x,y); }\n inline void point(float x,float y,float z){ if(PApplet::g_papplet) PApplet::g_papplet->point(x,y,z); }\n inline void triangle(float x1,float y1,float x2,float y2,float x3,float y3){ if(PApplet::g_papplet) PApplet::g_papplet->triangle(x1,y1,x2,y2,x3,y3); }\n inline void quad(float x1,float y1,float x2,float y2,float x3,float y3,float x4,float y4){ if(PApplet::g_papplet) PApplet::g_papplet->quad(x1,y1,x2,y2,x3,y3,x4,y4); }\n inline void arc(float x,float y,float w,float h,float s,float e){ if(PApplet::g_papplet) PApplet::g_papplet->arc(x,y,w,h,s,e); }\n inline void arc(float x,float y,float w,float h,float s,float e,int m){ if(PApplet::g_papplet) PApplet::g_papplet->arc(x,y,w,h,s,e,m); }\n inline void translate(float x,float y){ if(PApplet::g_papplet) PApplet::g_papplet->translate(x,y); }\n inline void translate(float x,float y,float z){ if(PApplet::g_papplet) PApplet::g_papplet->translate(x,y,z); }\n inline void rotate(float a){ if(PApplet::g_papplet) PApplet::g_papplet->rotate(a); }\n inline void scale(float s1,float s2){ if(PApplet::g_papplet) PApplet::g_papplet->scale(s1,s2); }\n inline void vertex(float x,float y){ if(PApplet::g_papplet) PApplet::g_papplet->vertex(x,y); }\n inline void vertex(float x,float y,float z){ if(PApplet::g_papplet) PApplet::g_papplet->vertex(x,y,z); }\n inline void vertex(float x,float y,float u,float v){ if(PApplet::g_papplet) PApplet::g_papplet->vertex(x,y,u,v); }\n inline void bezier(float x1,float y1,float cx1,float cy1,float cx2,float cy2,float x2,float y2){ if(PApplet::g_papplet) PApplet::g_papplet->bezier(x1,y1,cx1,cy1,cx2,cy2,x2,y2); }\n inline void curve(float x0,float y0,float x1,float y1,float x2,float y2,float x3,float y3){ if(PApplet::g_papplet) PApplet::g_papplet->curve(x0,y0,x1,y1,x2,y2,x3,y3); }\n inline void text(float v,float x,float y){ if(PApplet::g_papplet) PApplet::g_papplet->text(v,x,y); }\n inline void text(const std::string& s,float x,float y){ if(PApplet::g_papplet) PApplet::g_papplet->text(s,x,y); }\n inline void text(const std::string& s,float x,float y,float w,float h){ if(PApplet::g_papplet) PApplet::g_papplet->text(s,x,y,w,h); }\n inline float map(float v,float i0,float i1,float o0,float o1){ return PApplet::map(v,i0,i1,o0,o1); }\n inline float constrain(float v,float lo,float hi){ return PApplet::constrain(v,lo,hi); }\n inline float lerp(float a,float b,float t){ return PApplet::lerp(a,b,t); }\n inline void fill(float g){ if(PApplet::g_papplet) PApplet::g_papplet->fill(g); }\n inline void fill(float g,float a){ if(PApplet::g_papplet) PApplet::g_papplet->fill(g,a); }\n inline void fill(float r,float g,float b){ if(PApplet::g_papplet) PApplet::g_papplet->fill(r,g,b); }\n inline void fill(float r,float g,float b,float a){ if(PApplet::g_papplet) PApplet::g_papplet->fill(r,g,b,a); }\n inline void stroke(float g){ if(PApplet::g_papplet) PApplet::g_papplet->stroke(g); }\n inline void stroke(float g,float a){ if(PApplet::g_papplet) PApplet::g_papplet->stroke(g,a); }\n inline void stroke(float r,float g,float b){ if(PApplet::g_papplet) PApplet::g_papplet->stroke(r,g,b); }\n inline void stroke(float r,float g,float b,float a){ if(PApplet::g_papplet) PApplet::g_papplet->stroke(r,g,b,a); }\n inline void background(float g){ if(PApplet::g_papplet) PApplet::g_papplet->background(g); }\n inline void background(float g,float a){ if(PApplet::g_papplet) PApplet::g_papplet->background(g,a); }\n inline void background(float r,float g,float b){ if(PApplet::g_papplet) PApplet::g_papplet->background(r,g,b); }\n inline void background(float r,float g,float b,float a){ if(PApplet::g_papplet) PApplet::g_papplet->background(r,g,b,a); }\n inline void tint(float g){ if(PApplet::g_papplet) PApplet::g_papplet->tint(g); }\n inline void tint(float g,float a){ if(PApplet::g_papplet) PApplet::g_papplet->tint(g,a); }\n inline void tint(float r,float g,float b){ if(PApplet::g_papplet) PApplet::g_papplet->tint(r,g,b); }\n inline void tint(float r,float g,float b,float a){ if(PApplet::g_papplet) PApplet::g_papplet->tint(r,g,b,a); }\n inline void strokeWeight(float w){ if(PApplet::g_papplet) PApplet::g_papplet->strokeWeight(w); }\n} // namespace _api\n\n\n\n\ninline void PGraphics::beginDraw() {\n // Guard against beginDraw() called again before a matching endDraw().\n // savedViewport is a single field -- without this guard, a second\n // beginDraw() would overwrite the FIRST call\'s saved (correct) main-\n // canvas viewport with the buffer\'s OWN viewport (since that\'s what\n // the first beginDraw() just set), so the eventual endDraw() restores\n // the wrong thing entirely, corrupting the main canvas\'s viewport for\n // the rest of the frame (everything renders squished into a viewport\n // sized for the buffer instead of the real window). Calling\n // beginDraw() twice without an intervening endDraw() is unusual\n // sketch code to begin with, but it must not corrupt unrelated state.\n if (active) return;\n // ROOT-CAUSE FIX: previously captured "whatever the viewport happens\n // to be right now" via glGetIntegerv -- but if an EARLIER beginDraw()\n // (on this buffer or a different, abandoned one) was never matched\n // with endDraw(), the viewport at THIS moment may already be\n // corrupted (some other buffer\'s small viewport, not the real main\n // canvas\'s), and faithfully "restoring" that corrupted value just\n // propagates it forward into the next thing drawn -- this was the\n // actual cause of "the same line() call lands in a different place\n // depending on what PGraphics code ran earlier in the same frame."\n // Instead, always compute the TRUE main-canvas viewport directly\n // from PApplet\'s own known-good framebuffer dimensions, never trust\n // glGetIntegerv\'s possibly-already-wrong live value here.\n if (PApplet::g_papplet) {\n // fbW/fbH (the real framebuffer pixel size, accounting for\n // HiDPI) are a file-local static inside Processing.cpp, not\n // reachable from this header -- logicalW/logicalH ARE real\n // PApplet members though, and are exactly what _endDrawImpl()\n // already uses for restoring the projection\'s glOrtho call (see\n // the matching fix there), so using them here too keeps both\n // halves of the restore logically consistent with each other,\n // even though they\'re not pixel-perfect on a HiDPI display.\n savedViewport[0] = 0;\n savedViewport[1] = 0;\n savedViewport[2] = PApplet::g_papplet->logicalW;\n savedViewport[3] = PApplet::g_papplet->logicalH;\n } else {\n glGetIntegerv(GL_VIEWPORT, savedViewport);\n }\n // Render into the multisampled FBO when available (antialiased\n // drawing); the resolve target (fbo) gets filled via a blit in\n // _endDrawImpl(), not rendered into directly, when samples > 0.\n glBindFramebuffer(GL_FRAMEBUFFER, samples > 0 ? msaaFbo : fbo);\n glViewport(0, 0, width, height);\n glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity();\n if (is3D) {\n // BUG FIX: a P3D buffer previously got the SAME flat 2D ortho\n // projection as a plain 2D buffer (depth range -1..1), clipping\n // away any real 3D content entirely -- a box(80) translated even\n // slightly in Z extends far outside that paper-thin depth range\n // and never reaches the screen. This mirrors PApplet::\n // applyDefaultCamera()\'s real perspective setup, scoped to this\n // buffer\'s own width/height instead of the main canvas\'s\n // logicalW/logicalH.\n float eyeZ = ((float)height / 2.0f) / std::tan(PI * 60.0f / 360.0f);\n float near_ = eyeZ / 10.0f;\n float far_ = eyeZ * 10.0f;\n // glScalef(1.0f, -1.0f, 1.0f); // TEMPORARILY REMOVED for flip testing\n {\n // Inlined equivalent of _gluPerspective(60, width/height, near_, far_)\n // -- that helper is `static` (file-local) inside Processing.cpp,\n // not reachable from here, so the same small amount of matrix\n // construction is duplicated rather than changing its linkage\n // just for this one additional call site.\n double f = 1.0 / std::tan(60.0 * M_PI / 360.0);\n double aspect = (double)width / (double)height;\n double m[16] = {0};\n m[0] = f / aspect;\n m[5] = f;\n m[10] = (far_ + near_) / (near_ - far_);\n m[11] = -1.0;\n m[14] = (2.0 * far_ * near_) / (near_ - far_);\n glMultMatrixd(m);\n }\n glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();\n {\n // Inlined equivalent of _gluLookAt(width/2, height/2, eyeZ,\n // width/2, height/2, 0, 0, 1, 0)\n double ex = width/2.0, ey = height/2.0, ez = eyeZ;\n double cx = width/2.0, cy = height/2.0, cz = 0.0;\n double ux = 0.0, uy = 1.0, uz = 0.0;\n double fx = cx-ex, fy = cy-ey, fz = cz-ez;\n double fl = std::sqrt(fx*fx+fy*fy+fz*fz); fx/=fl; fy/=fl; fz/=fl;\n double sx = fy*uz - fz*uy, sy = fz*ux - fx*uz, sz = fx*uy - fy*ux;\n double sl = std::sqrt(sx*sx+sy*sy+sz*sz); sx/=sl; sy/=sl; sz/=sl;\n double ux2 = sy*fz - sz*fy, uy2 = sz*fx - sx*fz, uz2 = sx*fy - sy*fx;\n double m[16] = {\n sx, ux2, -fx, 0,\n sy, uy2, -fy, 0,\n sz, uz2, -fz, 0,\n 0, 0, 0, 1\n };\n glMultMatrixd(m);\n glTranslated(-ex, -ey, -ez);\n }\n glEnable(GL_DEPTH_TEST);\n glDepthFunc(GL_LESS);\n } else {\n glOrtho(0, width, height, 0, -1, 1);\n glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();\n glScalef(1.0f, -1.0f, 1.0f);\n glTranslatef(0.0f, -(float)height, 0.0f);\n }\n active = true;\n {\n GLint vp[4]; glGetIntegerv(GL_VIEWPORT, vp);\n double mv[16], proj[16];\n glGetDoublev(GL_MODELVIEW_MATRIX, mv);\n glGetDoublev(GL_PROJECTION_MATRIX, proj);\n PDEBUG("PGraphics::beginDraw: width=%d height=%d samples=%d msaaFbo=%u fbo=%u this=%p\\n",\n width, height, samples, msaaFbo, fbo, (void*)this);\n PDEBUG(" GL viewport: x=%d y=%d w=%d h=%d\\n", vp[0], vp[1], vp[2], vp[3]);\n PDEBUG(" proj[0]=%.6f proj[5]=%.6f proj[12]=%.6f proj[13]=%.6f\\n", proj[0], proj[5], proj[12], proj[13]);\n PDEBUG(" mv[0]=%.6f mv[5]=%.6f mv[12]=%.6f mv[13]=%.6f\\n", mv[0], mv[5], mv[12], mv[13]);\n }\n // Swap PApplet\'s CURRENT style out (stash it for restoration in\n // endDraw()) and swap THIS buffer\'s own remembered style in. On the\n // very first beginDraw() for a fresh buffer, myStyle still holds the\n // struct\'s default-initialized values (which match real Processing\'s\n // actual PGraphics defaults: black fill, no stroke, etc.) -- exactly\n // matching what a brand-new PGraphics should start with, independent\n // of whatever the main canvas\'s style happened to be.\n if (PApplet::g_papplet) {\n auto* p = PApplet::g_papplet;\n _savedMainStyle.fillR=p->fillR; _savedMainStyle.fillG=p->fillG; _savedMainStyle.fillB=p->fillB; _savedMainStyle.fillA=p->fillA;\n _savedMainStyle.strokeR=p->strokeR; _savedMainStyle.strokeG=p->strokeG; _savedMainStyle.strokeB=p->strokeB; _savedMainStyle.strokeA=p->strokeA;\n _savedMainStyle.strokeW=p->strokeW;\n _savedMainStyle.doFill=p->doFill; _savedMainStyle.doStroke=p->doStroke; _savedMainStyle.smoothing=p->smoothing;\n _savedMainStyle.currentRectMode=p->currentRectMode; _savedMainStyle.currentEllipseMode=p->currentEllipseMode; _savedMainStyle.currentImageMode=p->currentImageMode;\n _savedMainStyle.tintR=p->tintR; _savedMainStyle.tintG=p->tintG; _savedMainStyle.tintB=p->tintB; _savedMainStyle.tintA=p->tintA;\n _savedMainStyle.doTint=p->doTint;\n _savedMainStyle.colorModeVal=p->colorModeVal;\n _savedMainStyle.colorMaxH=p->colorMaxH; _savedMainStyle.colorMaxS=p->colorMaxS; _savedMainStyle.colorMaxB=p->colorMaxB; _savedMainStyle.colorMaxA=p->colorMaxA;\n _savedMainStyle.g_textSize=p->g_textSize;\n _savedMainStyle.g_textAlignX=p->g_textAlignX; _savedMainStyle.g_textAlignY=p->g_textAlignY;\n _savedMainStyle.g_textLeading=p->g_textLeading;\n\n p->fillR=myStyle.fillR; p->fillG=myStyle.fillG; p->fillB=myStyle.fillB; p->fillA=myStyle.fillA;\n p->strokeR=myStyle.strokeR; p->strokeG=myStyle.strokeG; p->strokeB=myStyle.strokeB; p->strokeA=myStyle.strokeA;\n p->strokeW=myStyle.strokeW;\n p->doFill=myStyle.doFill; p->doStroke=myStyle.doStroke; p->smoothing=myStyle.smoothing;\n p->currentRectMode=myStyle.currentRectMode; p->currentEllipseMode=myStyle.currentEllipseMode; p->currentImageMode=myStyle.currentImageMode;\n p->tintR=myStyle.tintR; p->tintG=myStyle.tintG; p->tintB=myStyle.tintB; p->tintA=myStyle.tintA;\n p->doTint=myStyle.doTint;\n p->colorModeVal=myStyle.colorModeVal;\n p->colorMaxH=myStyle.colorMaxH; p->colorMaxS=myStyle.colorMaxS; p->colorMaxB=myStyle.colorMaxB; p->colorMaxA=myStyle.colorMaxA;\n p->g_textSize=myStyle.g_textSize;\n p->g_textAlignX=myStyle.g_textAlignX; p->g_textAlignY=myStyle.g_textAlignY;\n p->g_textLeading=myStyle.g_textLeading;\n }\n myStyle.initialized = true;\n}\ninline void PGraphics::_endDrawImpl() {\n glMatrixMode(GL_PROJECTION); glPopMatrix();\n glMatrixMode(GL_MODELVIEW); glPopMatrix();\n // Resolve the multisampled content down into the plain texture-backed\n // FBO that drawPGraphicsRect actually samples from. Without this\n // blit, anything rendered into msaaFbo would never reach the texture\n // used for display -- the buffer would appear blank/stale.\n if (samples > 0 && msaaFbo != 0) {\n glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFbo);\n glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);\n GLenum readStatus = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER);\n GLenum drawStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);\n glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,\n GL_COLOR_BUFFER_BIT, GL_LINEAR);\n GLenum err = glGetError();\n PDEBUG("_endDrawImpl blit-resolve: readStatus=0x%x drawStatus=0x%x glError=0x%x width=%d height=%d is3D=%d\\n",\n readStatus, drawStatus, err, width, height, is3D);\n }\n {\n // Direct pixel readback from the buffer\'s own resolve-target FBO,\n // completely bypassing the texture-sampling/display path -- this\n // tells us definitively whether the rendered content actually\n // exists in the framebuffer\'s color attachment at all, or\n // whether the bug is specifically in how that texture gets\n // SAMPLED later (drawPGraphicsRect), not in the rendering itself.\n glBindFramebuffer(GL_FRAMEBUFFER, fbo);\n unsigned char px[4] = {0,0,0,0};\n int cx = width/2, cy = height/2; // center of buffer, should be inside the cube\n glReadPixels(cx, cy, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, px);\n PDEBUG("_endDrawImpl PIXEL READBACK at buffer center (%d,%d): r=%d g=%d b=%d a=%d (fbo=%u)\\n",\n cx, cy, px[0], px[1], px[2], px[3], fbo);\n }\n glBindFramebuffer(GL_FRAMEBUFFER, 0);\n active = false;\n // Save whatever style this buffer ended up with (so the NEXT\n // beginDraw() on this same buffer resumes where it left off, matching\n // real Processing semantics -- a PGraphics remembers its own style\n // across multiple begin/end cycles), then restore the main canvas\'s\n // style exactly as it was before beginDraw() touched it.\n if (PApplet::g_papplet) {\n auto* p = PApplet::g_papplet;\n myStyle.fillR=p->fillR; myStyle.fillG=p->fillG; myStyle.fillB=p->fillB; myStyle.fillA=p->fillA;\n myStyle.strokeR=p->strokeR; myStyle.strokeG=p->strokeG; myStyle.strokeB=p->strokeB; myStyle.strokeA=p->strokeA;\n myStyle.strokeW=p->strokeW;\n myStyle.doFill=p->doFill; myStyle.doStroke=p->doStroke; myStyle.smoothing=p->smoothing;\n myStyle.currentRectMode=p->currentRectMode; myStyle.currentEllipseMode=p->currentEllipseMode; myStyle.currentImageMode=p->currentImageMode;\n myStyle.tintR=p->tintR; myStyle.tintG=p->tintG; myStyle.tintB=p->tintB; myStyle.tintA=p->tintA;\n myStyle.doTint=p->doTint;\n myStyle.colorModeVal=p->colorModeVal;\n myStyle.colorMaxH=p->colorMaxH; myStyle.colorMaxS=p->colorMaxS; myStyle.colorMaxB=p->colorMaxB; myStyle.colorMaxA=p->colorMaxA;\n myStyle.g_textSize=p->g_textSize;\n myStyle.g_textAlignX=p->g_textAlignX; myStyle.g_textAlignY=p->g_textAlignY;\n myStyle.g_textLeading=p->g_textLeading;\n\n p->fillR=_savedMainStyle.fillR; p->fillG=_savedMainStyle.fillG; p->fillB=_savedMainStyle.fillB; p->fillA=_savedMainStyle.fillA;\n p->strokeR=_savedMainStyle.strokeR; p->strokeG=_savedMainStyle.strokeG; p->strokeB=_savedMainStyle.strokeB; p->strokeA=_savedMainStyle.strokeA;\n p->strokeW=_savedMainStyle.strokeW;\n p->doFill=_savedMainStyle.doFill; p->doStroke=_savedMainStyle.doStroke; p->smoothing=_savedMainStyle.smoothing;\n p->currentRectMode=_savedMainStyle.currentRectMode; p->currentEllipseMode=_savedMainStyle.currentEllipseMode; p->currentImageMode=_savedMainStyle.currentImageMode;\n p->tintR=_savedMainStyle.tintR; p->tintG=_savedMainStyle.tintG; p->tintB=_savedMainStyle.tintB; p->tintA=_savedMainStyle.tintA;\n p->doTint=_savedMainStyle.doTint;\n p->colorModeVal=_savedMainStyle.colorModeVal;\n p->colorMaxH=_savedMainStyle.colorMaxH; p->colorMaxS=_savedMainStyle.colorMaxS; p->colorMaxB=_savedMainStyle.colorMaxB; p->colorMaxA=_savedMainStyle.colorMaxA;\n p->g_textSize=_savedMainStyle.g_textSize;\n p->g_textAlignX=_savedMainStyle.g_textAlignX; p->g_textAlignY=_savedMainStyle.g_textAlignY;\n p->g_textLeading=_savedMainStyle.g_textLeading;\n }\n glViewport(savedViewport[0],savedViewport[1],savedViewport[2],savedViewport[3]);\n glMatrixMode(GL_PROJECTION);glLoadIdentity();\n // BUG FIX: savedViewport[2]/[3] are FRAMEBUFFER PIXEL dimensions\n // (fbW/fbH), captured via glGetIntegerv(GL_VIEWPORT,...) -- correct\n // for restoring the viewport itself, but WRONG for glOrtho. The main\n // render loop deliberately uses fbW/fbH for glViewport but\n // logicalW/logicalH for glOrtho specifically so sketch coordinates\n // map 1:1 regardless of HiDPI/Retina display scaling (see the\n // matching comment in PApplet::run()). Using savedViewport\'s pixel\n // dimensions for glOrtho here made every main-canvas draw call after\n // endDraw() appear at the wrong scale/position on any HiDPI display.\n if (PApplet::g_papplet) {\n auto* p = PApplet::g_papplet;\n glOrtho(0, p->logicalW, p->logicalH, 0, -1, 1);\n } else {\n glOrtho(0, savedViewport[2], savedViewport[3], 0, -1, 1);\n }\n glMatrixMode(GL_MODELVIEW);glLoadIdentity();\n}\ninline void PGraphics::background(float g) { if(PApplet::g_papplet) PApplet::g_papplet->background(g); }\ninline void PGraphics::background(float r, float g2, float b) { if(PApplet::g_papplet) PApplet::g_papplet->background(r,g2,b,255); }\ninline void PGraphics::background(float r, float g2, float b, float a){ if(PApplet::g_papplet) PApplet::g_papplet->background(r,g2,b,a); }\ninline void PGraphics::fill(float g) { if(PApplet::g_papplet) PApplet::g_papplet->fill(g); }\ninline void PGraphics::fill(float r, float g2, float b) { if(PApplet::g_papplet) PApplet::g_papplet->fill(r,g2,b); }\ninline void PGraphics::fill(float r, float g2, float b, float a) { if(PApplet::g_papplet) PApplet::g_papplet->fill(r,g2,b,a); }\ninline void PGraphics::noFill() { if(PApplet::g_papplet) PApplet::g_papplet->noFill(); }\ninline void PGraphics::stroke(float g) { if(PApplet::g_papplet) PApplet::g_papplet->stroke(g); }\ninline void PGraphics::stroke(float r, float g2, float b) { if(PApplet::g_papplet) PApplet::g_papplet->stroke(r,g2,b); }\ninline void PGraphics::noStroke() { if(PApplet::g_papplet) PApplet::g_papplet->noStroke(); }\ninline void PGraphics::strokeWeight(float w) { if(PApplet::g_papplet) PApplet::g_papplet->strokeWeight(w); }\ninline void PGraphics::ellipse(float x, float y, float w2, float h2){ if(PApplet::g_papplet) PApplet::g_papplet->ellipse(x,y,w2,h2); }\ninline void PGraphics::rect(float x, float y, float w2, float h2) { if(PApplet::g_papplet) PApplet::g_papplet->rect(x,y,w2,h2); }\ninline void PGraphics::line(float x1, float y1, float x2, float y2) { if(PApplet::g_papplet) PApplet::g_papplet->line(x1,y1,x2,y2); }\ninline void PGraphics::point(float x, float y) { if(PApplet::g_papplet) PApplet::g_papplet->point(x,y); }\ninline void PGraphics::triangle(float x1,float y1,float x2,float y2,float x3,float y3){ if(PApplet::g_papplet) PApplet::g_papplet->triangle(x1,y1,x2,y2,x3,y3); }\ninline void PGraphics::text(const std::string& s, float x, float y) { if(PApplet::g_papplet) PApplet::g_papplet->text(s,x,y); }\ninline void PGraphics::textSize(float size) { if(PApplet::g_papplet) PApplet::g_papplet->textSize(size); }\ninline void PGraphics::textAlign(int alignX) { if(PApplet::g_papplet) PApplet::g_papplet->textAlign(alignX,0); }\ninline void PGraphics::textAlign(int alignX, int alignY) { if(PApplet::g_papplet) PApplet::g_papplet->textAlign(alignX,alignY); }\ninline void PGraphics::translate(float x, float y) { if(PApplet::g_papplet) PApplet::g_papplet->translate(x,y); }\ninline void PGraphics::rotate(float a) { if(PApplet::g_papplet) PApplet::g_papplet->rotate(a); }\ninline void PGraphics::scale(float s) { if(PApplet::g_papplet) PApplet::g_papplet->scale(s); }\ninline void PGraphics::pushMatrix() { if(PApplet::g_papplet) PApplet::g_papplet->pushMatrix(); }\ninline void PGraphics::popMatrix() { if(PApplet::g_papplet) PApplet::g_papplet->popMatrix(); }\ninline void PGraphics::beginShape() { if(PApplet::g_papplet) PApplet::g_papplet->beginShape(); }\ninline void PGraphics::endShape(int mode) { if(PApplet::g_papplet) PApplet::g_papplet->endShape(mode); }\ninline void PGraphics::vertex(float x, float y) { if(PApplet::g_papplet) PApplet::g_papplet->vertex(x,y); }\ninline void PGraphics::clear() { if(PApplet::g_papplet) PApplet::g_papplet->clear(); }\ninline void PGraphics::translate(float x, float y, float z) { if(PApplet::g_papplet) PApplet::g_papplet->translate(x,y,z); }\ninline void PGraphics::rotateX(float angle) { if(PApplet::g_papplet) PApplet::g_papplet->rotateX(angle); }\ninline void PGraphics::rotateY(float angle) { if(PApplet::g_papplet) PApplet::g_papplet->rotateY(angle); }\ninline void PGraphics::rotateZ(float angle) { if(PApplet::g_papplet) PApplet::g_papplet->rotateZ(angle); }\ninline void PGraphics::box(float size) { if(PApplet::g_papplet) PApplet::g_papplet->box(size); }\ninline void PGraphics::box(float w, float h, float d) { if(PApplet::g_papplet) PApplet::g_papplet->box(w,h,d); }\ninline void PGraphics::sphere(float r) { if(PApplet::g_papplet) PApplet::g_papplet->sphere(r); }\ninline void PGraphics::lights() { if(PApplet::g_papplet) PApplet::g_papplet->lights(); }\ninline void PGraphics::noLights() { if(PApplet::g_papplet) PApplet::g_papplet->noLights(); }\ninline void PGraphics::ambientLight(float r, float g, float b) { if(PApplet::g_papplet) PApplet::g_papplet->ambientLight(r,g,b); }\ninline void PGraphics::ambientLight(float r, float g, float b, float x, float y, float z) { if(PApplet::g_papplet) PApplet::g_papplet->ambientLight(r,g,b,x,y,z); }\ninline void PGraphics::directionalLight(float r, float g, float b, float nx, float ny, float nz) { if(PApplet::g_papplet) PApplet::g_papplet->directionalLight(r,g,b,nx,ny,nz); }\ninline void PGraphics::pointLight(float r, float g, float b, float x, float y, float z) { if(PApplet::g_papplet) PApplet::g_papplet->pointLight(r,g,b,x,y,z); }\ninline void PGraphics::spotLight(float r, float g, float b, float x, float y, float z, float nx, float ny, float nz, float angle, float conc) { if(PApplet::g_papplet) PApplet::g_papplet->spotLight(r,g,b,x,y,z,nx,ny,nz,angle,conc); }\ninline void PGraphics::lightFalloff(float c, float l, float q) { if(PApplet::g_papplet) PApplet::g_papplet->lightFalloff(c,l,q); }\ninline void PGraphics::lightSpecular(float r, float g, float b) { if(PApplet::g_papplet) PApplet::g_papplet->lightSpecular(r,g,b); }\n\n\ninline void link(const std::string& url) {\n#ifdef _WIN32\n ShellExecuteA(nullptr,"open",url.c_str(),nullptr,nullptr,SW_SHOWNORMAL);\n#elif defined(__APPLE__)\n system(("open "+url).c_str());\n#else\n system(("xdg-open "+url+" &").c_str());\n#endif\n}\ninline void link(const char* url){link(std::string(url));}\n\n\n\n} // namespace Processing\n'
def main():
target = os.path.join(find_root(), "src", "Processing.h")
current = open(target, encoding="utf-8").read()
if current == CONTENT:
print("Already up to date.")
return
open(target, "w", encoding="utf-8").write(CONTENT)
print(f"Wrote {target} ({len(CONTENT)} bytes)")
print("No jar rebuild needed. Recompile your sketch in Processing.")
if __name__ == "__main__":
main()