diff --git a/_config.yml b/_config.yml index 61677f5..84c1aef 100644 --- a/_config.yml +++ b/_config.yml @@ -8,14 +8,15 @@ # Basic Site Settings locale : "en-US" -site_theme : "default" -title : "Bryce Grant" -title_separator : "-" -name : &name "Your Name" -description : &description "personal description" -url : https://bryceag11.github.io # the base hostname & protocol for your site e.g. "https://[your GitHub username].github.io" or if you already have some other page hosted on Github then use "https://[your GitHub username].github.io/[Your Repo Name]" -baseurl : "" # the subpath of your site, e.g. "/blog" -repository : "bryceag11/bryceag11.github.io" +site_theme : "default" +title : "Bryce Grant" +title_separator : "-" +name : &name "Your Name" +description : &description "personal description" +url : https://bryceag11.github.io # the base hostname & protocol for your site e.g. "https://[your GitHub username].github.io" or if you already have some other page hosted on Github then use "https://[your GitHub username].github.io/[Your Repo Name]" +baseurl : "" # the subpath of your site, e.g. "/blog" +repository : "bryceag11/bryceag11.github.io" +show_masthead : false # Site Author - The following control what appear as part of the author content on the side bar. # If a field is blank the icon and link will not appear, otherwise it will be shown. diff --git a/_data/home_highlights.yml b/_data/home_highlights.yml new file mode 100644 index 0000000..3469d53 --- /dev/null +++ b/_data/home_highlights.yml @@ -0,0 +1,24 @@ +- title: "QUAN" + description: "Quaternion Approximate Networks" + media: "/assets/media/highlights/quan.mp4" + # poster: "/assets/media/highlights/quan.png" + media_type: "video" + links: + - label: "Read more" + url: "https://arxiv.org/abs/2509.05512" +- title: "Sheaf Interpretability" + description: "Gluing Neural Interpretations: Sheaf Cohomology, Hodge Decomposition, and Energies for Cross-Context Consistency" + media: "/assets/media/highlights/ThickGradientSheafFix.mp4" + # poster: "ThickGradientSheafFix.mp4" + media_type: "video" + # links: + # - label: "Project" + # url: "/projects/" +- title: "Quat Viz" + description: "Quaternion Visualizations" + media: "/assets/media/highlights/quan.mp4" + poster: "/assets/media/highlights/quan.png" + media_type: "video" + links: + - label: "Demo" + url: "https://cwru-aism.github.io/QUANpaper/" diff --git a/_data/home_publications.yml b/_data/home_publications.yml new file mode 100644 index 0000000..170743b --- /dev/null +++ b/_data/home_publications.yml @@ -0,0 +1,34 @@ +- title: "Quaternion Approximation Networks for Enhanced Image Classification and Oriented Object Detection" + authors: "Bryce Grant, Peng Wang" + venue: "IROS 2025 · Oral" + image: "/assets/media/highlights/quan.mp4" + summary: "Rotation-aware perception layers composed with quaternion algebra so we keep spatial intuition while training compact real-valued networks" + links: + - label: "Paper" + url: "https://arxiv.org/abs/2509.05512" + - label: "Project Page" + url: "https://cwru-aism.github.io/QUANpaper/" + - label: "Code" + url: "https://github.com/bryceag11/QUAN_ultralytics" +# - title: "Causal PointNet for Fixtures" +# authors: "Bryce Grant, Peng Wang" +# venue: "IROS Workshop 2023" +# summary: "Counterfactual interventions on point clouds to predict when fixtures fail and to stage new grasping strategies before cutting tooling." +# links: +# - label: "Paper" +# url: "/files/Causal_PointNet.pdf" +# - label: "Project" +# url: "/projects/" +# - label: "Code" +# url: "https://github.com/bryceag11" +# - title: "UR5 Assembly Coach" +# authors: "Bryce Grant, AISM Lab" +# venue: "In progress" +# summary: "A multimodal coach that narrates assembly steps and pushes haptic cues to the robot as students learn manufacturing tasks." +# links: +# - label: "Summary" +# url: "/portfolio/" +# - label: "Project" +# url: "/projects/" +# - label: "Code" +# url: "https://github.com/bryceag11" diff --git a/_data/home_sections.yml b/_data/home_sections.yml new file mode 100644 index 0000000..373c858 --- /dev/null +++ b/_data/home_sections.yml @@ -0,0 +1,38 @@ +projects: + title: "Projects" + intro: "Selected research and class projects." + cards: + - title: "Causal DenseFusion" + image: "/images/causal_pointnet.png" + meta: "Computer Vision · Causal Inference" + date: "2024" + description: "Refines 6D pose estimates using causal interventions and backdoor adjustments based on structural causal models. Improves robustness to viewpoint ambiguity and symmetry." + badges: ["PointNet", "Causal ML"] + links: + - label: "PDF" + url: "/files/Causal_PointNet.pdf" + - label: "Code" + url: "https://github.com/bryceag11/CausalDenseFusion" + - title: "Probabilistic Digital Twin" + image: "/images/ros_stack.png" + meta: "Robotics · Probabilistic Modeling" + date: "2024" + description: "ROS2-integrated Dynamic Bayesian Network for real-time fault detection in Universal Robots using Unscented Kalman Filtering to track friction, damping, and wear parameters." + badges: ["ROS2", "UKF", "DBN"] + links: + - label: "PDF" + url: "/files/PGM_URDT.pdf" + - label: "Code" + url: "https://github.com/bryceag11/JARVIS" + - title: "Obstructive Sleep Apnea Detection" + image: "/images/500x300.png" + meta: "Machine Learning · Healthcare" + date: "2024" + description: "Machine learning system for detecting obstructive sleep apnea events using physiological signal processing and deep learning techniques for real-time classification of respiratory disturbances." + badges: ["Deep Learning", "Signal Processing"] + links: + - label: "Code" + url: "https://github.com/bryceag11/OSA" + footer_link: + label: "Older Projects" + url: "/projects/" diff --git a/_data/home_updates.yml b/_data/home_updates.yml new file mode 100644 index 0000000..027dc07 --- /dev/null +++ b/_data/home_updates.yml @@ -0,0 +1,18 @@ +- date: "Oct 2025" + emoji: "🤖" + text: "Attending IROS in Hanzghou to present QUAN" + url: "https://cwru-aism.github.io/QUANpaper/" +- date: "Apr 2025" + emoji: "🧭" + text: "Awarded the NSF GRFP" + url: "https://www.research.gov/grfp/AwardeeList.do?method=loadAwardeeList" +- date: "Aug 2024" + emoji: "🎓" + text: "Started my PhD at Case Western Reserve University" +- date: "May - Aug 2024" + emoji: "🧠" + text: "PhD intern at HP focused on multi-agents, RAG, and anomaly detection" +- date: "May 2024" + emoji: "😼" + text: "Graduated from the University of Kentucky with a dual BS in Electrical & Computer Engineering" + diff --git a/_data/navigation.yml b/_data/navigation.yml index b1a819f..d92e73a 100644 --- a/_data/navigation.yml +++ b/_data/navigation.yml @@ -17,11 +17,13 @@ main: # - title: "Teaching" # url: /teaching/ - # - title: "Portfolio" - # url: /portfolio/ - - title: "Projects" - url: /projects/ - # - title: "Blog Posts" + # - title: "Portfolio" + # url: /portfolio/ + - title: "Projects" + url: /projects/ + - title: "Photography" + url: /photography/ + # - title: "Blog Posts" # url: /year-archive/ - title: "CV" diff --git a/_data/photography.yml b/_data/photography.yml new file mode 100644 index 0000000..76a8f88 --- /dev/null +++ b/_data/photography.yml @@ -0,0 +1,412 @@ +film: + - slug: fall-2019 + title: "Fall 2019" + location: "Lexington, Kentucky" + description: "My first dedicated 35mm project—a study in grain, late harvest light, and the cables that crisscross industrial yards." + camera: "Canon AE-1 · Kodak Portra 400" + coords: + lat: 38.0406 + lng: -84.5037 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#f59e0b", "#c026d3"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Switchyard lines at dusk." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Detail study in rust + glass." + - slug: rodchenko-inspiration + title: "Rodchenko Inspiration" + location: "Moscow, Russia" + description: "Pulled from my Russian Culture final project—recreating Rodchenko angles with modern architecture and brutalist stairs." + camera: "Pentax K1000 · Ilford HP5" + coords: + lat: 55.7558 + lng: 37.6173 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#334155", "#94a3b8"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Subway descent shot with a 28mm lens." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Diagonal skylines inspired by Rodchenko." + - slug: atl + title: "ATL" + location: "Atlanta, Georgia" + description: "Transit pylons, MARTA shadows, and the soft neon of the BeltLine." + camera: "Canon AE-1 · Cinestill 800T" + coords: + lat: 33.749 + lng: -84.388 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#fb7185", "#fcd34d"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Midtown traffic study." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Blue hour reflections." + - slug: maui + title: "Maui" + location: "Maui, Hawai'i" + description: "Salt-stained cameras, volcano hikes, and the sound of waves on 35mm." + camera: "Olympus XA · Kodak Ektar 100" + coords: + lat: 20.7984 + lng: -156.3319 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#0ea5e9", "#38bdf8"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Haleakalā sunrise." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Paia surf break." + - slug: berlin + title: "Berlin" + location: "Berlin, Germany" + description: "Textures from Kreuzberg to the Brandenburg Gate, mixing Bauhaus lines with candid portraits." + camera: "Canon AE-1 · Kodak Gold" + coords: + lat: 52.52 + lng: 13.405 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#2563eb", "#ef4444"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Tempelhofer Feld silhouettes." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Neukölln stairwells." + - slug: france + title: "France" + location: "Paris + Lyon" + description: "A study on cafe stillness, metro motion blur, and industrial coastlines." + camera: "Canon AE-1 · Fuji Pro 400H" + coords: + lat: 48.8566 + lng: 2.3522 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#f472b6", "#c084fc"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Pont Neuf long exposure." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Lyon rail yard." + - slug: poland + title: "Poland" + location: "Warsaw, Poland" + description: "Concrete, color blocking, and everyday poetry while documenting the Vistula." + camera: "Pentax K1000 · Ilford Delta 400" + coords: + lat: 52.2297 + lng: 21.0122 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#a855f7", "#6366f1"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Science Palace symmetry study." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Foggy Vistula morning." + - slug: ukraine + title: "Ukraine" + location: "Kyiv & Lviv" + description: "Documenting resilience through portrait sessions and architectural fragments." + camera: "Canon AE-1 · Kodak Tri-X" + coords: + lat: 50.4501 + lng: 30.5234 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#fb923c", "#fcd34d"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Golden hour in Podil." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Opera house scaffolding." + - slug: pnw + title: "PNW" + location: "Pacific Northwest" + description: "Foggy evergreen mornings, ferries, and the color of drizzle." + camera: "Nikon FM2 · Kodak Ultramax" + coords: + lat: 47.6062 + lng: -122.3321 + cover: "/assets/images/photography/film-placeholder.svg" + palette: ["#0ea5e9", "#a7f3d0"] + gallery: + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Puget Sound ferry ride." + - src: "/assets/images/photography/film-placeholder.svg" + caption: "Columbia River Gorge overlook." + +digital: + - slug: us-abandoned + title: "US Abandoned" + location: "Detroit · Appalachia" + description: "Infrared-inspired digital set exploring abandoned manufacturing sites." + camera: "Sony A7 IV" + coords: + lat: 42.3314 + lng: -83.0458 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#22d3ee", "#0f172a"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Packard plant atrium scan." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Coal tipple geometry." + - slug: haleakala + title: "Haleakalā National Park" + location: "Maui, Hawai'i" + description: "Volcanic crater sunrises and otherworldly landscapes above the clouds." + camera: "Sony A7R III" + coords: + lat: 20.7097 + lng: -156.1720 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#f97316", "#fde047"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Crater rim at dawn." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Silversword plants." + - slug: mount-rainier + title: "Mount Rainier National Park" + location: "Washington" + description: "Alpine meadows and glacial peaks in the Pacific Northwest." + camera: "Sony A7R III" + coords: + lat: 46.8800 + lng: -121.7269 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#60a5fa", "#e0f2fe"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Paradise meadows wildflowers." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Nisqually Glacier study." + - slug: crater-lake + title: "Crater Lake National Park" + location: "Oregon" + description: "The deepest lake in the US, ringed by volcanic cliffs and pristine blue waters." + camera: "Sony A7R III" + coords: + lat: 42.8684 + lng: -122.1685 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#3b82f6", "#1e40af"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Wizard Island reflection." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Rim drive panorama." + - slug: olympic + title: "Olympic National Park" + location: "Washington" + description: "Temperate rainforests, rugged coastline, and mountain wilderness." + camera: "Sony A7R III" + coords: + lat: 47.8021 + lng: -123.6044 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#10b981", "#065f46"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Hoh Rainforest moss." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Ruby Beach sea stacks." + - slug: banff + title: "Banff National Park" + location: "Alberta, Canada" + description: "Turquoise lakes, jagged peaks, and Canadian Rockies grandeur." + camera: "Sony A7R III" + coords: + lat: 51.4968 + lng: -115.9281 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#06b6d4", "#0e7490"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Moraine Lake blue hour." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Peyto Lake overlook." + - slug: glacier-np + title: "Glacier National Park" + location: "Montana" + description: "Crown of the Continent with alpine lakes and Going-to-the-Sun Road." + camera: "Sony A7R III" + coords: + lat: 48.7596 + lng: -113.7870 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#7dd3fc", "#0c4a6e"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Hidden Lake overlook." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Many Glacier sunrise." + - slug: sequoia + title: "Sequoia National Park" + location: "California" + description: "Giant sequoias and the largest trees on Earth towering in the Sierra Nevada." + camera: "Sony A7R III" + coords: + lat: 36.4864 + lng: -118.5658 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#92400e", "#44403c"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "General Sherman Tree." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Giant Forest canopy." + - slug: redwood + title: "Redwood National Park" + location: "California" + description: "Ancient coastal redwoods and fog-draped forest cathedrals." + camera: "Sony A7R III" + coords: + lat: 41.2132 + lng: -124.0046 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#14532d", "#15803d"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Fern Canyon walls." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Tall Trees Grove." + - slug: rocky-mountain + title: "Rocky Mountain National Park" + location: "Colorado" + description: "High alpine tundra, elk herds, and continental divide vistas." + camera: "Sony A7R III" + coords: + lat: 40.3428 + lng: -105.6836 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#818cf8", "#4f46e5"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Bear Lake reflection." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Trail Ridge Road summit." + - slug: china + title: "China" + location: "Shanghai & Shenzhen" + description: "Architecture, street food vendors, and ferry commutes captured during robotics exchange trips." + camera: "Fujifilm X-Pro3" + coords: + lat: 31.2304 + lng: 121.4737 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#14b8a6", "#115e59"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Bund skyline reflection." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Shenzhen manufacturing floors." + - slug: japan + title: "Japan" + location: "Tokyo & Kyoto" + description: "A mix of neon alleys, Shinkansen pans, and quiet temples." + camera: "Fujifilm X100V" + coords: + lat: 35.6762 + lng: 139.6503 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#f472b6", "#d946ef"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Shibuya crossing long exposure." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Arashiyama bamboo groove." + - slug: morocco + title: "Morocco" + location: "Marrakesh & Sahara" + description: "Dust storms, Atlas mountain roads, and sunrise dunes in rich desert palettes." + camera: "Sony A7 III" + coords: + lat: 31.6295 + lng: -7.9811 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#f97316", "#ea580c"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Djemaa el-Fna rooftop." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Merzouga dunes at dawn." + - slug: portugal + title: "Portugal" + location: "Lisbon & Porto" + description: "Azulejo tiles, tram lines, and golden hour over the Douro River." + camera: "Sony A7 IV" + coords: + lat: 38.7223 + lng: -9.1393 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#0ea5e9", "#f59e0b"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Lisbon tram 28 study." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Porto riverside sunset." + - slug: costa-rica-panama + title: "Costa Rica & Panama" + location: "San José · Panama City" + description: "Cloud forests, canal locks, and vibrant street markets between two oceans." + camera: "Fujifilm X-T4" + coords: + lat: 9.7489 + lng: -83.7534 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#10b981", "#059669"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Monteverde canopy walk." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Panama Canal transit." + - slug: belize-guatemala + title: "Belize & Guatemala" + location: "Belize City · Antigua" + description: "Mayan ruins, Caribbean coastlines, and volcanic landscapes in Central America." + camera: "Sony A7 III" + coords: + lat: 15.7835 + lng: -90.2308 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#14b8a6", "#0d9488"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Tikal sunrise through fog." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Belize barrier reef." + - slug: western-europe + title: "Western Europe" + location: "Amsterdam · Paris · Geneva · Berlin" + description: "Digital medium-format stitches dedicated to reflections, trains, and living with water across Europe's cultural capitals." + camera: "GFX 50R" + coords: + lat: 52.3676 + lng: 4.9041 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#7dd3fc", "#1d4ed8"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Canal mirror lines." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Eiffel Tower dawn." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Geneva lake symmetry." + - slug: central-eastern-europe + title: "Central & Eastern Europe" + location: "Prague · Kraków · Zakopane · Lviv · Kyiv · Rivne" + description: "Night walks with a wireframe aesthetic—mixing lidar captures with still photography across historic cities and mountain towns." + camera: "Sony A7S III" + coords: + lat: 50.0755 + lng: 14.4378 + cover: "/assets/images/photography/digital-placeholder.svg" + palette: ["#06b6d4", "#0f172a"] + gallery: + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Charles Bridge lidar overlay." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Tatra Mountains twilight." + - src: "/assets/images/photography/digital-placeholder.svg" + caption: "Kyiv golden domes." diff --git a/_includes/home/highlights.html b/_includes/home/highlights.html new file mode 100644 index 0000000..c6d8e25 --- /dev/null +++ b/_includes/home/highlights.html @@ -0,0 +1,48 @@ +{% assign highlights = site.data.home_highlights | default: [] %} +{% if highlights.size > 0 %} +
+ {% for highlight in highlights %} + {% assign media_src = highlight.media %} + {% if media_src %} + {% if media_src contains '://' %} + {% assign media_url = media_src %} + {% else %} + {% assign media_url = media_src | relative_url %} + {% endif %} + {% endif %} +
+ {% assign first_link = highlight.links | first %} + {% assign highlight_link = nil %} + {% if first_link %} + {% assign highlight_link = first_link.url %} + {% unless highlight_link contains '://' %} + {% assign highlight_link = highlight_link | relative_url %} + {% endunless %} + {% endif %} + {% if highlight_link %} + + {% else %} + + {% endif %} +
+

{{ highlight.title }}

+ {% if highlight.description %} +

{{ highlight.description }}

+ {% endif %} +
+
+ {% endfor %} +
+{% endif %} diff --git a/_includes/home/publication-preview.html b/_includes/home/publication-preview.html new file mode 100644 index 0000000..a6278e5 --- /dev/null +++ b/_includes/home/publication-preview.html @@ -0,0 +1,33 @@ +{% assign publications = site.publications | sort: 'date' | reverse %} +{% assign preview = publications | slice: 0, 3 %} +{% if preview.size > 0 %} +
+ {% for item in preview %} +
+
+

{{ item.venue }}

+ {% if item.date %} +

{{ item.date | date: '%b %Y' }}

+ {% endif %} +
+
+

{{ item.title }}

+ {% if item.excerpt %} +

{{ item.excerpt | strip_html | truncate: 220 }}

+ {% endif %} +
+ Read more → + {% if item.paperurl %} + Paper + {% endif %} +
+
+
+ {% endfor %} +
+
+ View full publications +
+{% else %} +

No publications to display yet. Add entries to the _publications collection.

+{% endif %} diff --git a/_includes/home/publications.html b/_includes/home/publications.html new file mode 100644 index 0000000..82c8e4d --- /dev/null +++ b/_includes/home/publications.html @@ -0,0 +1,137 @@ +{% assign pubs = site.data.home_publications | default: [] %} +{% if pubs.size > 0 %} + + +
+ {% for pub in pubs %} +
+ {% if pub.image %} +
+ {% if pub.image contains '.mp4' or pub.image contains '.webm' or pub.image contains '.mov' %} + + {% else %} + {{ pub.title }} + {% endif %} +
+ {% endif %} +
+

{{ pub.title }}

+ {% if pub.authors %} +

{{ pub.authors }}

+ {% endif %} + {% if pub.venue %} +

{{ pub.venue }}

+ {% endif %} + {% if pub.links %} + + {% endif %} +
+
+ {% endfor %} +
+{% else %} +

Add entries to _data/home_publications.yml to populate this section.

+{% endif %} diff --git a/_includes/home/section-cards.html b/_includes/home/section-cards.html new file mode 100644 index 0000000..63c867f --- /dev/null +++ b/_includes/home/section-cards.html @@ -0,0 +1,165 @@ +{% assign all_sections = site.data.home_sections %} +{% if include.section_id and all_sections and all_sections[include.section_id] %} + {% assign section = all_sections[include.section_id] %} + +
+
+ {% if include.kicker or section.kicker %} +

{{ include.kicker | default: section.kicker }}

+ {% endif %} +

{{ include.title | default: section.title }}

+ {% if section.intro %} +

{{ section.intro }}

+ {% endif %} +
+ {% if include.action_url %} + {{ include.action_label | default: 'View all' }} + {% endif %} +
+
+ {% for card in section.cards %} + {% assign url = card.link_url %} + {% if url %} + {% unless url contains '://' %} + {% assign url = url | relative_url %} + {% endunless %} + {% endif %} +
+ {% if card.image %} +
+ {{ card.title }} +
+ {% endif %} +
+ {% if card.meta %} +

{{ card.meta }}

+ {% endif %} + {% if card.date %} +

{{ card.date }}

+ {% endif %} +

{{ card.title }}

+ {% if card.description %} +

{{ card.description }}

+ {% endif %} +
+ {% if card.badges %} + + {% endif %} + {% if card.links %} + + {% endif %} +
+ {% endfor %} +
+ {% if section.footer_link %} + + {% endif %} +{% endif %} diff --git a/_includes/home/updates.html b/_includes/home/updates.html new file mode 100644 index 0000000..86182a4 --- /dev/null +++ b/_includes/home/updates.html @@ -0,0 +1,22 @@ +{% assign updates = site.data.home_updates | default: [] %} +{% if updates.size > 0 %} + +{% endif %} diff --git a/_includes/photography/gallery.html b/_includes/photography/gallery.html new file mode 100644 index 0000000..ae13435 --- /dev/null +++ b/_includes/photography/gallery.html @@ -0,0 +1,30 @@ +{% if include.entry %} + + +{% else %} +

The requested gallery could not be found. Please update _data/photography.yml.

+{% endif %} diff --git a/_layouts/default.html b/_layouts/default.html index 56f6996..a87d206 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -14,7 +14,9 @@ {% include browser-upgrade.html %} - {% include masthead.html %} + {% if site.show_masthead != false and page.hide_masthead != true %} + {% include masthead.html %} + {% endif %} {{ content }} diff --git a/_layouts/home.html b/_layouts/home.html new file mode 100644 index 0000000..05843d2 --- /dev/null +++ b/_layouts/home.html @@ -0,0 +1,9 @@ +--- +layout: default +--- + +{% include base_path %} + +
+ {{ content }} +
diff --git a/_pages/about.md b/_pages/about.md index 32bf9b0..829611b 100644 --- a/_pages/about.md +++ b/_pages/about.md @@ -1,16 +1,71 @@ ---- -permalink: / -title: "About Me" -author_profile: true -redirect_from: - - /about/ - - /about.html ---- - -I am a Ph.D. student at the [Electrical Computer and Systems Engineering Department](https://engineering.case.edu/electrical-computer-and-systems-engineering), [Case Western Reserve University](https://case.edu/) where I’m advised by Prof. [Peng “Edward” Wang](https://scholar.google.com/citations?user=4CbVWDcAAAAJ&hl=en). Previously, I received my bachelor's degree from the [Department of Electrical and Computer Engineering](https://engr.uky.edu/academics/departments/ece) at University of Kentucky. - -I am currently a research assistant in the Augmented Intelligence for Smart Manufacturing Lab (AISM), advised by Dr. Peng Wang. - -My research lies at the intersection of **robotic perception**, **geometry-aware learning**, and **sequential modeling**. I’m interested in building systems that reason about pose, motion, and uncertainty through geometric representations, causal inference, and generative models applied to assembly environments. I’m currently exploring semantic keypoint representations and sim2real transfer for robotic manipulation. - -Email: bag100@case.edu +--- +layout: home +permalink: / +title: "Bryce Grant" +author_profile: false +classes: home +hide_masthead: true +redirect_from: + - /about/ + - /about.html +--- + +
+
+

Bryce Grant

+
+

Electrical Engineering PhD Student

+

Case Western Reserve University

+
+

I am a 2nd year Ph.D. student at Case Western Reserve University where I’m advised by Prof. Peng “Edward” Wang. I received my dual B.S. in EE & CPE (Electrical and Computer Engineering) from the University of Kentucky in 2024. I’m currently funded by the NSF GRFP.

+

I’m interested in building robotic systems that reason about pose, motion, and uncertainty through geometric-aware learning, causal inference, and sequential modeling.

+

I’m currently working on semantic affordances and topology-informed interpretability for VLAs.

+ +
+
+ Bryce Grant +
+
+ + + +
+ + + + Photography +
+ +
+
+
+ {% include home/highlights.html %} +
+
+

Updates

+ {% include home/updates.html %} +
+
+ +
+ {% include home/publications.html %} +
+ +
+ {% include home/section-cards.html section_id="projects" %} +
+
+ + diff --git a/_pages/photography.md b/_pages/photography.md new file mode 100644 index 0000000..f439dc1 --- /dev/null +++ b/_pages/photography.md @@ -0,0 +1,94 @@ +--- +layout: single +title: "Photography" +permalink: /photography/ +classes: photography +author_profile: false +--- + +{% assign photo = site.data.photography %} + +
+
+

35mm & Digital Photography

+

Outside of my research, I love urbex, adrenaline, and film photography. Explore this to see what I've captured. UNDER CONSTRUCTION

+ +
+
+ +
+ + +
+ +
+
+ + +
+ +
+ +
+ {% for entry in photo.film %} + {% assign cover = entry.cover %} + {% if cover %} + {% unless cover contains '://' %} + {% assign cover = cover | relative_url %} + {% endunless %} + {% endif %} + + {% endfor %} +
+
+ +
+ +
+ {% for entry in photo.digital %} + {% assign cover = entry.cover %} + {% if cover %} + {% unless cover contains '://' %} + {% assign cover = cover | relative_url %} + {% endunless %} + {% endif %} + + {% endfor %} +
+
+ + + + + + diff --git a/_pages/projects.md b/_pages/projects.md index 2a24004..e22740a 100644 --- a/_pages/projects.md +++ b/_pages/projects.md @@ -1,48 +1,128 @@ --- layout: archive -title: "Projects" +title: "Older Projects" permalink: /projects/ -author_profile: true ---- - -Below are selected research and class projects. - ---- - -### Causal PointNet - -Refines 6D pose estimates using causal interventions and backdoor adjustments based on structural causal models. Improves robustness to viewpoint ambiguity and symmetry. - -[PDF](/files/Causal_PointNet.pdf) | [Code](https://github.com/bryceag11/CausalDenseFusion) - ---- - -### Probabilistic Digital Twin - -Developed a ROS2-integrated Dynamic Bayesian Network for real-time fault detection in Universal Robots. Used Unscented Kalman Filtering to track friction, damping, and wear parameters. - -[PDF](/files/PGM_URDT.pdf) | [Code](https://github.com/bryceag11/PGM_URDT) - ---- - -### Diffusion Models in Robotics - -Explores how Denoising Diffusion Probabilistic Models can be used to synthesize robot action policies from noise, using a NoProp training method adapted for stochastic control. - -[PDF](/files/DDPMs.pdf) | [Code](https://github.com/bryceag11/NoProp) - ---- - -### Just a Rather Very Intelligent System (J.A.R.V.I.S.) -J.A.R.V.I.S. is a social navigating robot designed to provide an immersive telepresence experience for remote users. As the navigation systems lead, I developed the robot's 3D SLAM, path planning, and autonomous navigation capabilities from the ground up - -[PDF](/files/FDR_Report.pdf) | [CODE](https://github.com/bryceag11/JARVIS.git) - - - +author_profile: false +classes: wide --- + + +

Personal projects from 2023-2024

+ +
+

No-Propagation Diffusion Models in Robotics

+
Machine Learning · Robotics · Stochastic Control
+
+ NoProp Architecture +
+

Explores Denoising Diffusion Probabilistic Models using a NoProp training method. Surveys their use in robotics.

+ +
+ +
+

J.A.R.V.I.S. Telehealth Robot

+
Robotics · Computer Vision · Autonomous Navigation
+
+ JARVIS Robot +
+

Social navigating robot designed to provide an immersive telepresence experience for remote users. Led development of the robot's 3D SLAM system with servo-mounted LiDAR, path planning algorithms, and autonomous navigation capabilities. Achieved real-time mapping and dynamic obstacle avoidance in healthcare environments.

+ +
+ +
+

ShopStock Embedded Cloud PCB

+
IoT · Embedded Systems · Cloud Integration
+
+ ShopStock Hardware +
+

IoT hardware bridge for retrofitting legacy point-of-sale systems with cloud-based inventory tracking. Built custom firmware for ESP-32 microcontroller enabling real-time inventory synchronization. Co-founded ShopStock LLC and deployed system across multiple retail locations.

+
Tech Stack: ESP-32 · C/C++ · Custom Firmware · Cloud Integration
+
+ +
+ ← Back to Home +
diff --git a/_sass/layout/_home.scss b/_sass/layout/_home.scss new file mode 100644 index 0000000..4ff6e02 --- /dev/null +++ b/_sass/layout/_home.scss @@ -0,0 +1,369 @@ +.home-wrapper { + max-width: 1150px; + margin: 0 auto; + padding: 0 1.5rem 4rem; +} + +.home-hero { + display: grid; + grid-template-columns: minmax(420px, 1.4fr) auto; + gap: 1.5rem; + padding: 2rem 2.5rem; + align-items: stretch; + background: linear-gradient(135deg, #0f172a, #1f3a68); + border-radius: 2rem; + color: #fff; + box-shadow: 0 25px 55px rgba(2, 6, 23, 0.4); +} + +.home-hero__text { + max-width: 820px; +} + +.home-hero h1 { + margin: 0 0 0.35rem; + font-size: 2rem; +} + +.home-hero__titles { + display: flex; + flex-wrap: wrap; + gap: 0.8rem; + font-size: 0.95rem; + font-weight: 600; + color: #e2e8f0; +} + +.home-hero__titles p { + margin: 0; +} + +.home-hero__summary { + font-size: 1rem; + line-height: 1.7; + margin: 1rem 0; + color: #e2e8f0; +} + +.home-contact-links { + display: flex; + flex-wrap: wrap; + gap: 0.6rem; + align-items: center; + font-weight: 600; + color: rgba(255, 255, 255, 0.7); +} + +.home-contact-links span { + color: rgba(255, 255, 255, 0.45); +} + +.home-contact-links a { + color: #fff; + text-decoration: none; + border-bottom: 1px solid rgba(255, 255, 255, 0.4); + padding-bottom: 2px; + transition: border-color 0.2s ease, color 0.2s ease; +} + +.home-contact-links a:hover, +.home-contact-links a:focus { + border-color: #fff; + color: #fff; +} + +.home-hero__media { + display: flex; + align-items: flex-start; + justify-content: flex-end; +} + +.home-hero__media img { + border-radius: 1.5rem; + width: 240px; + height: 240px; + object-fit: cover; + box-shadow: 0 25px 45px rgba(0, 0, 0, 0.35); +} + +.home-about { + max-width: 780px; + font-size: 1rem; + line-height: 1.7; + margin: 2rem 0; + color: #0f172a; +} + +.home-about a { + color: #2563eb; + text-decoration: underline; +} + +.home-section { + margin-bottom: 2.5rem; +} + +.home-section-header { + margin-bottom: 1rem; +} + +.home-section-kicker { + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 0.75rem; + color: #94a3b8; + margin-bottom: 0.3rem; +} + +.home-menu { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0.75rem; + margin: 1.5rem 0 2.5rem; +} + +.home-menu__item { + appearance: none; + border: none; + border-radius: 999px; + padding: 0.55rem 1.75rem; + background: #f1f5f9; + color: #0f172a; + font-weight: 600; + cursor: pointer; + transition: background 0.2s ease; +} + +.home-menu__item.is-active { + background: linear-gradient(135deg, #007acc, #5b8def); + color: #fff; +} + +.home-menu__item--link { + text-decoration: none; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.home-panels { + position: relative; +} + +.home-panel { + display: none; + animation: fadeIn 0.3s ease; +} + +.home-panel.is-active { + display: block; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.home-highlights-block { + margin-bottom: 1.5rem; +} + +.home-panel-updates { + margin-top: 1.25rem; + padding-top: 0.75rem; + border-top: 1px solid rgba(15, 23, 42, 0.08); +} + +.home-highlight-gifs { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + gap: 1rem; +} + +.home-highlight-gif { + display: flex; + flex-direction: column; + gap: 0.6rem; +} + +.home-highlight-gif__media { + display: block; + overflow: hidden; + border-radius: 1rem; + box-shadow: 0 15px 35px rgba(15, 23, 42, 0.2); +} + +.home-highlight-gif__media video, +.home-highlight-gif__media img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.home-highlight-gif__text h3 { + margin: 0; +} + +.home-highlight-gif__text p { + margin: 0.3rem 0 0; +} + +.home-publication-stack { + display: flex; + flex-direction: column; + gap: 1rem; + max-width: 1100px; + margin: 0 auto; +} + +.home-publication-card { + background: #fff; + border-radius: 1.25rem; + padding: 1.2rem 1.5rem; + box-shadow: 0 15px 30px rgba(15, 23, 42, 0.12); + display: flex; + gap: 1rem; + align-items: flex-start; +} + +.home-publication-headline { + min-width: 240px; +} + +.home-publication-headline h3 { + margin: 0; +} + +.home-publication-body { + flex: 1; + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.home-publication-venue, +.home-card-meta { + text-transform: uppercase; + font-size: 0.75rem; + letter-spacing: 0.08em; + color: #94a3b8; +} + +.home-publication-links, +.home-card-badges { + display: flex; + gap: 0.35rem; + flex-wrap: wrap; + list-style: none; + padding: 0; + margin: 0.3rem 0 0; +} + +.home-card-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 0.85rem; + max-width: 960px; + margin: 0 auto; +} + +.home-card { + background: #fff; + border-radius: 1rem; + padding: 1rem; + box-shadow: 0 12px 24px rgba(15, 23, 42, 0.12); + display: flex; + flex-direction: column; + gap: 0.45rem; +} + +.home-card-badges li { + background: #f1f5f9; + border-radius: 999px; + padding: 0.2rem 0.7rem; + font-size: 0.75rem; +} + +.home-card-date { + margin: 0; + font-size: 0.85rem; + color: #475569; +} + +.home-panel-updates .home-updates { + margin-bottom: 0; +} + +.home-updates { + list-style: none; + padding: 0; + margin: 0.8rem 0 0; + display: flex; + flex-direction: column; + gap: 0.6rem; +} + +.home-updates li { + padding-bottom: 0.4rem; + border-bottom: 1px solid rgba(15, 23, 42, 0.08); +} + +.home-update-line { + margin: 0; + display: flex; + align-items: center; + gap: 0.4rem; +} + +.home-updates-emoji { + font-size: 1.1rem; +} + +.home-update-line strong { + font-size: 0.9rem; +} + +.home-update-line a { + color: inherit; + text-decoration: underline; +} + +.btn--outline { + background: transparent; + color: #0f172a; + border: 1px solid rgba(15, 23, 42, 0.3); +} + +.btn.btn--small { + font-size: 0.8rem; + padding: 0.3rem 0.8rem; + border-radius: 999px; +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + border: 0; +} + +@media (max-width: 640px) { + .home-menu__item { + flex: 1 1 auto; + } + + .home-publication-card { + flex-direction: column; + } +} diff --git a/_sass/layout/_photography.scss b/_sass/layout/_photography.scss new file mode 100644 index 0000000..0977213 --- /dev/null +++ b/_sass/layout/_photography.scss @@ -0,0 +1,289 @@ +.photography .page__inner-wrap { + max-width: 1300px; +} + +.photography-hero { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 1.8rem; + padding: 0.5rem 0 1.5rem; + align-items: center; +} + +.photography-hero__eyebrow { + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 0.85rem; + color: #64748b; +} + +.photography-hero__note { + background: linear-gradient(135deg, #0f172a, #5b21b6); + color: #e0f2fe; + padding: 1.25rem; + border-radius: 1.25rem; + box-shadow: 0 20px 45px rgba(15, 23, 42, 0.2); + font-size: 0.95rem; +} + +.photography-cta { + margin-top: 1.25rem; + display: flex; + gap: 0.75rem; + flex-wrap: wrap; +} + +.photography-toggle { + display: inline-flex; + border-radius: 999px; + background: #edf2f7; + padding: 0.25rem; + margin-bottom: 1rem; + gap: 0.25rem; +} + +.photography-toggle__button { + border: none; + background: transparent; + border-radius: 999px; + padding: 0.4rem 1.5rem; + font-weight: 600; + color: #475569; + cursor: pointer; +} + +.photography-toggle__button.is-active { + background: #0f172a; + color: #fff; +} + +.photography-globe { + display: grid; + grid-template-columns: 2fr 1fr; + gap: 1.5rem; + align-items: stretch; + margin-bottom: 2.5rem; + position: relative; +} + +#photo-globe { + width: 100%; + max-width: 1400px; + min-height: 500px; + margin: 0 auto; + border-radius: 1.5rem; + background: radial-gradient(circle at top, #0f172a, #020617); + overflow: hidden; +} + +.photo-globe--fallback { + display: flex; + align-items: center; + justify-content: center; + text-align: center; + padding: 2rem; +} + +.photo-globe__fallback { + color: #e2e8f0; + font-weight: 600; +} + +.photography-globe__legend { + background: #fff; + border-radius: 1.25rem; + padding: 1.25rem; + box-shadow: 0 20px 40px rgba(15, 23, 42, 0.1); +} + +.photography-globe__note { + font-size: 0.85rem; + color: #64748b; + margin-top: 0.75rem; +} + +.photography-globe__legend ul { + list-style: none; + padding: 0; + margin: 0.75rem 0 0; +} + +.photography-globe__legend li { + display: flex; + align-items: center; + gap: 0.6rem; + font-size: 0.95rem; +} + +.legend-dot { + width: 0.9rem; + height: 0.9rem; + border-radius: 50%; + display: inline-flex; +} + +.legend-dot--film { + background: #fcd34d; +} + +.legend-dot--digital { + background: #38bdf8; +} + +.photo-globe-tooltip { + position: fixed; + background: linear-gradient(135deg, rgba(15, 23, 42, 0.95), rgba(30, 41, 59, 0.95)); + color: #f8fafc; + padding: 0.8rem; + border-radius: 0.8rem; + font-size: 0.85rem; + pointer-events: auto; + transform: translate(-50%, -120%); + white-space: nowrap; + z-index: 10; + backdrop-filter: blur(10px); + border: 1px solid rgba(148, 163, 184, 0.1); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); + cursor: pointer; + transition: all 0.2s ease; + + &:hover { + transform: translate(-50%, -120%) scale(1.05); + box-shadow: 0 25px 50px rgba(0, 0, 0, 0.4); + } + + img { + display: block; + margin: 0 auto; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); + } + + strong { + color: #fbbf24; + display: block; + margin-bottom: 4px; + } + + small { + display: block; + margin-top: 4px; + } +} + +.photo-card-grid { + display: none; +} + +.photo-card { + background: #fff; + border-radius: 1.15rem; + overflow: hidden; + box-shadow: 0 14px 30px rgba(15, 23, 42, 0.12); + transition: transform 0.2s ease, box-shadow 0.2s ease; +} + +.photo-card--digital { + border: 1px solid rgba(20, 184, 166, 0.25); +} + +.photo-card a { + color: inherit; + text-decoration: none; + display: flex; + flex-direction: column; + height: 100%; +} + +.photo-card-image { + padding-top: 56%; + background-size: cover; + background-position: center; +} + +.photo-card-body { + padding: 0.9rem 1rem 1.1rem; + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.photo-card-location { + margin-top: 0.2rem; + font-weight: 600; + color: #475569; +} + +.photo-card:hover { + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(15, 23, 42, 0.15); +} + +.photography-section { + margin-bottom: 2.5rem; +} + +.photography-section__kicker { + text-transform: uppercase; + letter-spacing: 0.1em; + font-size: 0.78rem; + color: #94a3b8; +} + +.photo-gallery-hero { + padding: 1rem 0 1.5rem; +} + +.photo-gallery-kicker { + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 0.8rem; + color: #94a3b8; +} + +.photo-gallery-location { + font-weight: 600; + color: #475569; +} + +.photo-gallery-meta { + list-style: none; + padding: 0; + margin: 0.5rem 0 0; + display: flex; + gap: 1rem; + flex-wrap: wrap; +} + +.photo-gallery-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 1.25rem; +} + +.photo-gallery-grid figure { + margin: 0; +} + +.photo-gallery-grid img { + width: 100%; + border-radius: 1rem; + box-shadow: 0 15px 30px rgba(15, 23, 42, 0.18); +} + +.photo-gallery-grid figcaption { + margin-top: 0.5rem; + font-size: 0.9rem; + color: #475569; +} + +@media (max-width: 900px) { + .photography-globe { + grid-template-columns: 1fr; + } +} + +@media (max-width: 600px) { + #photo-globe { + min-height: 450px; + } +} diff --git a/assets/css/main.scss b/assets/css/main.scss index e720745..a4e8f04 100644 --- a/assets/css/main.scss +++ b/assets/css/main.scss @@ -30,14 +30,16 @@ "layout/footer", "syntax", - "layout/forms", - - "layout/page", - "layout/archive", - "layout/sidebar", - - "vendor/font-awesome/fontawesome", - "vendor/font-awesome/solid", + "layout/forms", + + "layout/page", + "layout/archive", + "layout/sidebar", + "layout/home", + "layout/photography", + + "vendor/font-awesome/fontawesome", + "vendor/font-awesome/solid", "vendor/font-awesome/brands", "vendor/magnific-popup/magnific-popup" -; \ No newline at end of file +; diff --git a/assets/images/photography/digital-placeholder.svg b/assets/images/photography/digital-placeholder.svg new file mode 100644 index 0000000..a728d56 --- /dev/null +++ b/assets/images/photography/digital-placeholder.svg @@ -0,0 +1,10 @@ + + + + + + + + + Digital + diff --git a/assets/images/photography/film-placeholder.svg b/assets/images/photography/film-placeholder.svg new file mode 100644 index 0000000..b0a7776 --- /dev/null +++ b/assets/images/photography/film-placeholder.svg @@ -0,0 +1,10 @@ + + + + + + + + + 35mm + diff --git a/assets/js/home-tabs.js b/assets/js/home-tabs.js new file mode 100644 index 0000000..efe6974 --- /dev/null +++ b/assets/js/home-tabs.js @@ -0,0 +1,32 @@ +(function () { + const controls = document.querySelectorAll('[data-home-tab]'); + const panels = document.querySelectorAll('[data-home-panel]'); + if (!controls.length || !panels.length) return; + + function setActive(id) { + panels.forEach((panel) => { + const match = panel.dataset.homePanel === id; + panel.classList.toggle('is-active', match); + panel.setAttribute('aria-hidden', match ? 'false' : 'true'); + }); + + controls.forEach((control) => { + const match = control.dataset.homeTab === id; + control.classList.toggle('is-active', match); + control.setAttribute('aria-selected', match ? 'true' : 'false'); + control.setAttribute('tabindex', match ? '0' : '-1'); + }); + } + + controls.forEach((control) => { + control.addEventListener('click', () => setActive(control.dataset.homeTab)); + control.addEventListener('keydown', (event) => { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault(); + setActive(control.dataset.homeTab); + } + }); + }); + + setActive('highlights'); +})(); diff --git a/assets/js/photography-globe.js b/assets/js/photography-globe.js new file mode 100644 index 0000000..77e61eb --- /dev/null +++ b/assets/js/photography-globe.js @@ -0,0 +1,676 @@ +(function () { + function supportsWebGL() { + try { + const canvas = document.createElement('canvas'); + return !!window.WebGLRenderingContext && !!(canvas.getContext('webgl') || canvas.getContext('experimental-webgl')); + } catch (err) { + return false; + } + } + + function getDimensions(container) { + const parentWidth = container.clientWidth || container.parentElement?.clientWidth || 1000; + const parentHeight = container.clientHeight; + const computedHeight = parentHeight && parentHeight > 0 ? parentHeight : Math.round(parentWidth * 0.55); // More horizontal + return { width: parentWidth, height: computedHeight }; + } + + function showFallback(container, message) { + if (!container) return; + container.classList.add('photo-globe--fallback'); + container.innerHTML = `

${message}

`; + } + + function bootGlobe() { + const container = document.getElementById('photo-globe'); + if (!container || !window.PHOTOGRAPHY_DATA) { + console.log('Container or PHOTOGRAPHY_DATA not found'); + return; + } + + // Check if WebGL is supported first + if (!supportsWebGL()) { + showFallback(container, 'The interactive globe needs WebGL (browser graphics). The full photo list is still available below.'); + return; + } + + // More robust Three.js loading with timeout + const maxWaitTime = 10000; // 10 seconds max wait + const startTime = Date.now(); + + function checkAndInit() { + if (typeof window.THREE !== 'undefined') { + console.log('Three.js loaded successfully, initializing globe...'); + try { + initGlobe(container); + } catch (error) { + console.error('Error initializing globe:', error); + showFallback(container, 'Error initializing 3D globe. Please refresh the page.'); + } + } else if (Date.now() - startTime < maxWaitTime) { + // Keep trying for up to 10 seconds + setTimeout(checkAndInit, 100); + } else { + console.error('Three.js failed to load after 10 seconds'); + // Try loading Three.js one more time as last resort + if (!window.threeLoadAttempted) { + window.threeLoadAttempted = true; + const script = document.createElement('script'); + script.src = '/assets/js/vendor/three.min.js'; + script.onload = () => { + if (typeof window.THREE !== 'undefined') { + console.log('Three.js loaded via fallback'); + initGlobe(container); + } else { + showFallback(container, 'Unable to load 3D graphics library. Please check your connection and refresh.'); + } + }; + script.onerror = () => { + showFallback(container, 'Failed to load graphics support. Please refresh the page.'); + }; + document.head.appendChild(script); + } else { + showFallback(container, 'Loading graphics support failed. Please refresh the page.'); + } + } + } + + checkAndInit(); + } + + function initGlobe(container) { + const THREE = window.THREE; + const tooltip = document.getElementById('photo-globe-tooltip'); + const dims = getDimensions(container); + container.style.minHeight = `${dims.height}px`; + + const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: false }); + renderer.setPixelRatio(window.devicePixelRatio || 1); + renderer.setSize(dims.width, dims.height); + container.appendChild(renderer.domElement); + + const scene = new THREE.Scene(); + const camera = new THREE.PerspectiveCamera(45, dims.width / dims.height, 0.1, 100); + camera.position.z = 3.2; + + // Different lighting for each mode + const filmAmbient = new THREE.AmbientLight(0xffffff, 0.5); // Normal white light + const filmDirectional = new THREE.DirectionalLight(0xffffff, 0.6); // White light + filmDirectional.position.set(5, 3, 5); + + const digitalAmbient = new THREE.AmbientLight(0x6666ff, 0.6); // Brighter blue ambient + const digitalDirectional = new THREE.DirectionalLight(0x88aaff, 0.5); // Brighter moonlight + digitalDirectional.position.set(-5, 3, 5); + + // Create Earth globe with better geometry + const globeGeometry = new THREE.SphereGeometry(1, 128, 64); + + // Load Earth texture from online source + const loader = new THREE.TextureLoader(); + loader.crossOrigin = 'anonymous'; + + // Load day and night Earth textures + const earthDayUrl = 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_atmos_2048.jpg'; + const earthNightUrl = 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/textures/planets/earth_lights_2048.png'; + + const earthTexture = loader.load( + earthDayUrl, + () => console.log('Earth day texture loaded'), + undefined, + () => { + console.log('Failed to load online texture, using procedural fallback'); + createProceduralEarth(); + } + ); + + const nightTexture = loader.load( + earthNightUrl, + () => console.log('Earth night lights texture loaded'), + undefined, + () => { + console.log('Failed to load night lights texture'); + } + ); + + // Create day/night shader for both modes + const vertexShader = ` + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `; + + const fragmentShader = ` + uniform sampler2D dayMap; + uniform sampler2D nightMap; + uniform float sunLongitude; // Sun's current longitude based on UTC time + uniform float sepiaAmount; // 0.0 = no sepia, 1.0 = full sepia + + varying vec2 vUv; + + vec3 applySepia(vec3 color, float amount) { + // Sepia tone matrix + vec3 sepia; + sepia.r = dot(color, vec3(0.393, 0.769, 0.189)); + sepia.g = dot(color, vec3(0.349, 0.686, 0.168)); + sepia.b = dot(color, vec3(0.272, 0.534, 0.131)); + return mix(color, sepia, amount); + } + + void main() { + // Convert UV to longitude (-PI to PI) + // UV.x goes from 0 to 1, representing -180° to 180° + float longitude = (vUv.x - 0.5) * 2.0 * 3.14159265359; + + // Calculate angular difference from sun position + float angleDiff = longitude - sunLongitude; + + // Normalize angle difference to -PI to PI range + angleDiff = mod(angleDiff + 3.14159265359, 2.0 * 3.14159265359) - 3.14159265359; + + // Day when angular difference is within ±90° of sun position + float halfPi = 3.14159265359 / 2.0; + float dayAmount = smoothstep(halfPi + 0.3, halfPi - 0.3, abs(angleDiff)); + + // Sample both textures + vec3 dayColor = texture2D(dayMap, vUv).rgb; + vec3 nightColor = texture2D(nightMap, vUv).rgb; + + // Mix based on sun position + vec3 finalColor = mix(nightColor, dayColor, dayAmount); + + // Apply sepia if requested + finalColor = applySepia(finalColor, sepiaAmount); + + gl_FragColor = vec4(finalColor, 1.0); + } + `; + + // Calculate sun's longitude based on current UTC time + // At UTC noon (12:00), sun is at Prime Meridian (0° longitude) + // At UTC midnight (0:00), sun is at antimeridian (180° or -180° longitude) + function getSunLongitude() { + const now = new Date(); + const hours = now.getUTCHours() + now.getUTCMinutes() / 60; + // Sun moves 360° in 24 hours = 15° per hour + // At hour 0 (midnight UTC) -> 180° (PI radians) + // At hour 12 (noon UTC) -> 0° (0 radians) + const longitude = ((12 - hours) / 12) * Math.PI; + return longitude; + } + + // Film material with sepia shader + const filmShaderMaterial = new THREE.ShaderMaterial({ + uniforms: { + dayMap: { value: earthTexture }, + nightMap: { value: nightTexture }, + sunLongitude: { value: getSunLongitude() }, + sepiaAmount: { value: 0.6 } // Light sepia for film + }, + vertexShader: vertexShader, + fragmentShader: fragmentShader + }); + + // Digital material without sepia + const digitalMaterial = new THREE.ShaderMaterial({ + uniforms: { + dayMap: { value: earthTexture }, + nightMap: { value: nightTexture }, + sunLongitude: { value: getSunLongitude() }, + sepiaAmount: { value: 0.0 } // No sepia for digital + }, + vertexShader: vertexShader, + fragmentShader: fragmentShader + }); + + // Update sun position every hour for realistic day/night cycle + setInterval(() => { + if (filmShaderMaterial.uniforms && digitalMaterial.uniforms) { + const newLongitude = getSunLongitude(); + filmShaderMaterial.uniforms.sunLongitude.value = newLongitude; + digitalMaterial.uniforms.sunLongitude.value = newLongitude; + } + }, 120000); // 2 minutes for testing, change to 3600000 for 1 hour + + // Fallback procedural Earth function + function createProceduralEarth() { + const canvas = document.createElement('canvas'); + canvas.width = 2048; + canvas.height = 1024; + const ctx = canvas.getContext('2d'); + + // Draw realistic ocean + ctx.fillStyle = '#1e3a5f'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + // Draw more realistic continents + ctx.fillStyle = '#2d5016'; + + // Draw landmasses with more detail + // North America + ctx.beginPath(); + ctx.moveTo(300, 300); + ctx.quadraticCurveTo(400, 250, 500, 350); + ctx.quadraticCurveTo(450, 450, 350, 500); + ctx.quadraticCurveTo(250, 400, 300, 300); + ctx.fill(); + + // South America + ctx.beginPath(); + ctx.moveTo(450, 600); + ctx.quadraticCurveTo(500, 650, 480, 800); + ctx.quadraticCurveTo(430, 850, 400, 800); + ctx.quadraticCurveTo(400, 700, 450, 600); + ctx.fill(); + + // Africa + ctx.beginPath(); + ctx.moveTo(1000, 400); + ctx.quadraticCurveTo(1100, 350, 1050, 500); + ctx.quadraticCurveTo(1080, 700, 1000, 750); + ctx.quadraticCurveTo(950, 650, 950, 500); + ctx.quadraticCurveTo(980, 400, 1000, 400); + ctx.fill(); + + // Europe + ctx.beginPath(); + ctx.moveTo(1000, 300); + ctx.quadraticCurveTo(1100, 280, 1150, 350); + ctx.quadraticCurveTo(1100, 380, 1000, 350); + ctx.fill(); + + // Asia + ctx.beginPath(); + ctx.moveTo(1200, 300); + ctx.quadraticCurveTo(1500, 250, 1600, 400); + ctx.quadraticCurveTo(1550, 500, 1400, 450); + ctx.quadraticCurveTo(1250, 400, 1200, 300); + ctx.fill(); + + // Australia + ctx.beginPath(); + ctx.ellipse(1500, 750, 120, 70, 0, 0, Math.PI * 2); + ctx.fill(); + + const fallbackTexture = new THREE.CanvasTexture(canvas); + filmShaderMaterial.uniforms.dayMap.value = fallbackTexture; + filmShaderMaterial.uniforms.nightMap.value = fallbackTexture; + digitalMaterial.uniforms.dayMap.value = fallbackTexture; + digitalMaterial.uniforms.nightMap.value = fallbackTexture; + } + + const globe = new THREE.Mesh(globeGeometry, filmShaderMaterial); + scene.add(globe); + + // Add initial film lighting + scene.add(filmAmbient, filmDirectional); + + const markersGroup = new THREE.Group(); + scene.add(markersGroup); + + // Create pin geometry (bright red ball head with silver stem) + const markerHeadGeometry = new THREE.SphereGeometry(0.018, 16, 16); + const markerStemGeometry = new THREE.CylinderGeometry(0.002, 0.003, 0.06, 8); + + // Materials for pins - bright red head, silver stem + const markerHeadMaterial = new THREE.MeshStandardMaterial({ + color: 0xff0000, // Bright red + emissive: 0xff3333, + emissiveIntensity: 0.5, // Brighter glow + metalness: 0.2, + roughness: 0.3 + }); + + const markerStemMaterial = new THREE.MeshStandardMaterial({ + color: 0xc0c0c0, // Silver + emissive: 0xffffff, + emissiveIntensity: 0.1, + metalness: 0.95, + roughness: 0.05 + }); + + const markerMap = new Map(); + const markers = { film: [], digital: [] }; + + function latLngToVector3(lat, lng, radius = 1.01) { + const phi = (90 - lat) * (Math.PI / 180); + const theta = (lng + 180) * (Math.PI / 180); + const x = -(radius * Math.sin(phi) * Math.cos(theta)); + const z = radius * Math.sin(phi) * Math.sin(theta); + const y = radius * Math.cos(phi); + return new THREE.Vector3(x, y, z); + } + + function createMarker(entry, type) { + // Create a container group for positioning + const containerGroup = new THREE.Group(); + + // Create the pin group that will be animated + const pinGroup = new THREE.Group(); + + // Create marker head (red sphere at TOP of pin) + const markerHead = new THREE.Mesh(markerHeadGeometry.clone(), markerHeadMaterial.clone()); + markerHead.position.y = 0; // Head at origin of pin + + // Create marker stem (silver cylinder going into Earth) + const markerStem = new THREE.Mesh(markerStemGeometry.clone(), markerStemMaterial.clone()); + markerStem.position.y = -0.03; // Half the stem height + + // Add both to the pin group + pinGroup.add(markerHead); + pinGroup.add(markerStem); + + // Position pin so it's mostly inserted + pinGroup.position.y = -0.035; // Push pin down into globe (inserted position) + + // Add pin group to container + containerGroup.add(pinGroup); + + // Position the container at the correct location on surface + const coords = entry.coords || { lat: 0, lng: 0 }; + const position = latLngToVector3(coords.lat, coords.lng, 1.0); // On surface + containerGroup.position.copy(position); + + // Make the container face toward the Earth's core (origin) + containerGroup.lookAt(0, 0, 0); + + // Store data including the pin group for animation + containerGroup.userData = { + ...entry, + type, + head: markerHead, + pinGroup: pinGroup, + defaultY: -0.035, // Inserted position + hoveredY: 0.01 // Pulled out position + }; + + markersGroup.add(containerGroup); + markers[type].push(containerGroup); + markerMap.set(entry.slug, containerGroup); + } + + (window.PHOTOGRAPHY_DATA.film || []).forEach((entry) => createMarker(entry, 'film')); + (window.PHOTOGRAPHY_DATA.digital || []).forEach((entry) => createMarker(entry, 'digital')); + + let currentMode = 'film'; + function setMode(mode) { + currentMode = mode; + globe.material = mode === 'film' ? filmShaderMaterial : digitalMaterial; + + // Switch lighting and background based on mode + if (mode === 'film') { + scene.remove(digitalAmbient, digitalDirectional); + scene.add(filmAmbient, filmDirectional); + renderer.setClearColor(0x0a0a0a, 1.0); // Dark background + } else { + // Digital mode - real-time day/night Earth + scene.remove(filmAmbient, filmDirectional); + scene.add(digitalAmbient, digitalDirectional); + renderer.setClearColor(0x000000, 1.0); // Black space + } + + markers.film.forEach((marker) => (marker.visible = mode === 'film')); + markers.digital.forEach((marker) => (marker.visible = mode === 'digital')); + container.dataset.mode = mode; + document + .querySelectorAll('[data-photo-mode]') + .forEach((button) => button.classList.toggle('is-active', button.dataset.photoMode === mode)); + } + + setMode('film'); + + const buttons = document.querySelectorAll('[data-photo-mode]'); + buttons.forEach((button) => + button.addEventListener('click', () => { + setMode(button.dataset.photoMode); + }) + ); + + let rotationY = 0; + let rotationX = 0; + let autoRotate = 0.0015; + let isDragging = false; + let lastX = 0; + let lastY = 0; + + function animate() { + requestAnimationFrame(animate); + if (!isDragging) { + rotationY += autoRotate; + } + markersGroup.rotation.y = rotationY; + markersGroup.rotation.x = rotationX; + globe.rotation.y = rotationY; + globe.rotation.x = rotationX; + renderer.render(scene, camera); + } + + animate(); + + let clickStartTime; + let clickStartMarker; + + function onPointerDown(event) { + clickStartTime = Date.now(); + clickStartMarker = hoveredMarker; + isDragging = true; + lastX = event.clientX; + lastY = event.clientY; + } + + function onPointerMove(event) { + if (isDragging) { + const deltaX = event.clientX - lastX; + const deltaY = event.clientY - lastY; + rotationY += deltaX * 0.005; + rotationX += deltaY * 0.005; + rotationX = Math.max(Math.min(rotationX, Math.PI / 2.5), -Math.PI / 2.5); + lastX = event.clientX; + lastY = event.clientY; + } + handleHover(event); + } + + function onPointerUp() { + isDragging = false; + + // Check if this was a click (not a drag) + const clickDuration = Date.now() - clickStartTime; + if (clickDuration < 200 && clickStartMarker && clickStartMarker === hoveredMarker) { + // Navigate to the photo album + const slug = hoveredMarker.userData.slug; + const type = hoveredMarker.userData.type; + if (slug) { + const url = `/photography/${type}/${slug}/`; + window.location.href = url; + } + } + } + + renderer.domElement.addEventListener('pointerdown', onPointerDown); + renderer.domElement.addEventListener('pointerleave', () => { + if (!isDragging) { + autoRotate = 0.0015; + if (hoveredMarker) { + // Animate pin back down + if (hoveredMarker.userData.pinGroup) { + hoveredMarker.userData.pinGroup.position.y = hoveredMarker.userData.defaultY; + } + if (hoveredMarker.userData.head) { + hoveredMarker.userData.head.material.emissiveIntensity = 0.5; + } + hoveredMarker = null; + } + clearTooltip(); + } + }); + window.addEventListener('pointermove', onPointerMove); + window.addEventListener('pointerup', onPointerUp); + + window.addEventListener('resize', () => { + const { width, height } = getDimensions(container); + container.style.minHeight = `${height}px`; + camera.aspect = width / height; + camera.updateProjectionMatrix(); + renderer.setSize(width, height); + }); + + const raycaster = new THREE.Raycaster(); + const mouse = new THREE.Vector2(); + let hoveredMarker = null; + + function updateTooltip(marker) { + if (!tooltip || !marker) return; + const vector = marker.position.clone(); + vector.applyMatrix4(markersGroup.matrixWorld); + vector.project(camera); + const rect = renderer.domElement.getBoundingClientRect(); + + // Account for scroll position + const scrollX = window.pageXOffset || document.documentElement.scrollLeft; + const scrollY = window.pageYOffset || document.documentElement.scrollTop; + + const x = ((vector.x + 1) / 2) * rect.width + rect.left + scrollX; + const y = ((-vector.y + 1) / 2) * rect.height + rect.top + scrollY; + + tooltip.style.left = `${x}px`; + tooltip.style.top = `${y}px`; + tooltip.style.transform = `translate(-50%, -120%)`; + + // Enhanced tooltip with photo preview + const userData = marker.userData; + const coverImage = userData.cover ? `${userData.title}` : ''; + const cameraInfo = userData.camera ? `${userData.camera}
` : ''; + + tooltip.innerHTML = ` +
+ ${coverImage} + ${userData.title}
+ ${userData.location}
+ ${cameraInfo} + Click to view album +
+ `; + tooltip.style.cursor = 'pointer'; + tooltip.hidden = false; + } + + function clearTooltip() { + if (tooltip) { + tooltip.hidden = true; + } + } + + function handleHover(event) { + const rect = renderer.domElement.getBoundingClientRect(); + mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; + mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; + raycaster.setFromCamera(mouse, camera); + const visibleMarkers = markers[currentMode]; + + // Check for intersections with pin groups (recursive to check children) + const intersects = raycaster.intersectObjects(visibleMarkers, true); + + if (intersects.length > 0) { + // Find the pin group that contains the intersected object + let pinGroup = intersects[0].object; + while (pinGroup.parent && !pinGroup.userData.type) { + pinGroup = pinGroup.parent; + } + + if (hoveredMarker && hoveredMarker !== pinGroup) { + // Reset previous marker + if (hoveredMarker.userData.pinGroup) { + hoveredMarker.userData.pinGroup.position.y = hoveredMarker.userData.defaultY; + } + if (hoveredMarker.userData.head) { + hoveredMarker.userData.head.material.emissiveIntensity = 0.5; + } + } + + hoveredMarker = pinGroup; + + // Animate pin pulling out + if (hoveredMarker.userData.pinGroup) { + hoveredMarker.userData.pinGroup.position.y = hoveredMarker.userData.hoveredY; + } + + // Make the head glow more when hovered + if (hoveredMarker.userData.head) { + hoveredMarker.userData.head.material.emissiveIntensity = 0.8; + } + + autoRotate = 0; + updateTooltip(hoveredMarker); + } else if (!isDragging) { + if (hoveredMarker) { + // Animate pin back down + if (hoveredMarker.userData.pinGroup) { + hoveredMarker.userData.pinGroup.position.y = hoveredMarker.userData.defaultY; + } + if (hoveredMarker.userData.head) { + hoveredMarker.userData.head.material.emissiveIntensity = 0.5; + } + hoveredMarker = null; + } + autoRotate = 0.0015; + clearTooltip(); + } + } + + const cards = document.querySelectorAll('.photo-card'); + cards.forEach((card) => { + const slug = card.dataset.photoMarker; + card.addEventListener('mouseenter', () => focusMarker(slug)); + card.addEventListener('focus', () => focusMarker(slug)); + card.addEventListener('mouseleave', () => resetCardHighlight(slug)); + card.addEventListener('blur', () => resetCardHighlight(slug)); + }); + + function focusMarker(slug) { + const marker = markerMap.get(slug); + if (!marker) return; + if (marker.userData.type !== currentMode) { + setMode(marker.userData.type); + } + rotationY = -Math.atan2(marker.position.z, marker.position.x) + Math.PI / 2; + rotationX = Math.asin(marker.position.y / marker.position.length()); + + if (hoveredMarker) { + hoveredMarker.scale.set(1, 1, 1); + if (hoveredMarker.userData.head) { + hoveredMarker.userData.head.material.emissiveIntensity = 0.3; + } + } + + hoveredMarker = marker; + marker.scale.set(1.8, 1.8, 1.8); + if (marker.userData.head) { + marker.userData.head.material.emissiveIntensity = 0.6; + } + autoRotate = 0; + updateTooltip(marker); + } + + function resetCardHighlight(slug) { + const marker = markerMap.get(slug); + if (marker && marker === hoveredMarker) { + hoveredMarker.scale.set(1, 1, 1); + if (hoveredMarker.userData.head) { + hoveredMarker.userData.head.material.emissiveIntensity = 0.3; + } + hoveredMarker = null; + clearTooltip(); + autoRotate = 0.0015; + } + } + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', bootGlobe); + } else { + bootGlobe(); + } +})(); diff --git a/assets/media/highlights/ThickGradientSheafFix.mp4 b/assets/media/highlights/ThickGradientSheafFix.mp4 new file mode 100644 index 0000000..83cb0b6 Binary files /dev/null and b/assets/media/highlights/ThickGradientSheafFix.mp4 differ diff --git a/assets/media/highlights/quan.mp4 b/assets/media/highlights/quan.mp4 new file mode 100644 index 0000000..00d7ac6 Binary files /dev/null and b/assets/media/highlights/quan.mp4 differ diff --git a/assets/media/highlights/quan.png b/assets/media/highlights/quan.png new file mode 100644 index 0000000..bf649e1 Binary files /dev/null and b/assets/media/highlights/quan.png differ diff --git a/images/shopstock.jpeg b/images/shopstock.jpeg new file mode 100644 index 0000000..8713cf8 Binary files /dev/null and b/images/shopstock.jpeg differ diff --git a/photography/digital/canadian-rockies.md b/photography/digital/canadian-rockies.md new file mode 100644 index 0000000..178cfae --- /dev/null +++ b/photography/digital/canadian-rockies.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "Canadian Rockies" +permalink: /photography/digital/canadian-rockies/ +slug: canadian-rockies +collection_type: digital +author_profile: false +--- + +{% assign entry = site.data.photography.digital | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="digital" collection_title="Digital expeditions" %} diff --git a/photography/digital/central-america.md b/photography/digital/central-america.md new file mode 100644 index 0000000..4aa36ff --- /dev/null +++ b/photography/digital/central-america.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "Central America" +permalink: /photography/digital/central-america/ +slug: central-america +collection_type: digital +author_profile: false +--- + +{% assign entry = site.data.photography.digital | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="digital" collection_title="Digital expeditions" %} diff --git a/photography/digital/central-eastern-europe.md b/photography/digital/central-eastern-europe.md new file mode 100644 index 0000000..8b4767d --- /dev/null +++ b/photography/digital/central-eastern-europe.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "Central & Eastern Europe" +permalink: /photography/digital/central-eastern-europe/ +slug: central-eastern-europe +collection_type: digital +author_profile: false +--- + +{% assign entry = site.data.photography.digital | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="digital" collection_title="Digital expeditions" %} diff --git a/photography/digital/china.md b/photography/digital/china.md new file mode 100644 index 0000000..1127a46 --- /dev/null +++ b/photography/digital/china.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "China" +permalink: /photography/digital/china/ +slug: china +collection_type: digital +author_profile: false +--- + +{% assign entry = site.data.photography.digital | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="digital" collection_title="Digital expeditions" %} diff --git a/photography/digital/japan.md b/photography/digital/japan.md new file mode 100644 index 0000000..f7f8fb3 --- /dev/null +++ b/photography/digital/japan.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "Japan" +permalink: /photography/digital/japan/ +slug: japan +collection_type: digital +author_profile: false +--- + +{% assign entry = site.data.photography.digital | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="digital" collection_title="Digital expeditions" %} diff --git a/photography/digital/morocco.md b/photography/digital/morocco.md new file mode 100644 index 0000000..14d96a1 --- /dev/null +++ b/photography/digital/morocco.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "Morocco" +permalink: /photography/digital/morocco/ +slug: morocco +collection_type: digital +author_profile: false +--- + +{% assign entry = site.data.photography.digital | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="digital" collection_title="Digital expeditions" %} diff --git a/photography/digital/us-abandoned.md b/photography/digital/us-abandoned.md new file mode 100644 index 0000000..d537918 --- /dev/null +++ b/photography/digital/us-abandoned.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "US Abandoned" +permalink: /photography/digital/us-abandoned/ +slug: us-abandoned +collection_type: digital +author_profile: false +--- + +{% assign entry = site.data.photography.digital | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="digital" collection_title="Digital expeditions" %} diff --git a/photography/digital/us-national-parks.md b/photography/digital/us-national-parks.md new file mode 100644 index 0000000..3a7c54f --- /dev/null +++ b/photography/digital/us-national-parks.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "US National Parks" +permalink: /photography/digital/us-national-parks/ +slug: us-national-parks +collection_type: digital +author_profile: false +--- + +{% assign entry = site.data.photography.digital | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="digital" collection_title="Digital expeditions" %} diff --git a/photography/digital/western-europe.md b/photography/digital/western-europe.md new file mode 100644 index 0000000..3e137dc --- /dev/null +++ b/photography/digital/western-europe.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "Western Europe" +permalink: /photography/digital/western-europe/ +slug: western-europe +collection_type: digital +author_profile: false +--- + +{% assign entry = site.data.photography.digital | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="digital" collection_title="Digital expeditions" %} diff --git a/photography/film/atl.md b/photography/film/atl.md new file mode 100644 index 0000000..1094726 --- /dev/null +++ b/photography/film/atl.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "ATL" +permalink: /photography/film/atl/ +slug: atl +collection_type: film +author_profile: false +--- + +{% assign entry = site.data.photography.film | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="film" collection_title="35mm film" %} diff --git a/photography/film/berlin.md b/photography/film/berlin.md new file mode 100644 index 0000000..49659d4 --- /dev/null +++ b/photography/film/berlin.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "Berlin" +permalink: /photography/film/berlin/ +slug: berlin +collection_type: film +author_profile: false +--- + +{% assign entry = site.data.photography.film | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="film" collection_title="35mm film" %} diff --git a/photography/film/central_america.md b/photography/film/central_america.md new file mode 100644 index 0000000..5e58ac0 --- /dev/null +++ b/photography/film/central_america.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "Central America" +permalink: /photography/film/central_america/ +slug: central_america +collection_type: film +author_profile: false +--- + +{% assign entry = site.data.photography.film | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="film" collection_title="35mm film" %} diff --git a/photography/film/fall-2019.md b/photography/film/fall-2019.md new file mode 100644 index 0000000..4133a2a --- /dev/null +++ b/photography/film/fall-2019.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "Fall 2019" +permalink: /photography/film/fall-2019/ +slug: fall-2019 +collection_type: film +author_profile: false +--- + +{% assign entry = site.data.photography.film | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="film" collection_title="35mm film" %} diff --git a/photography/film/france.md b/photography/film/france.md new file mode 100644 index 0000000..889a49a --- /dev/null +++ b/photography/film/france.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "France" +permalink: /photography/film/france/ +slug: france +collection_type: film +author_profile: false +--- + +{% assign entry = site.data.photography.film | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="film" collection_title="35mm film" %} diff --git a/photography/film/maui.md b/photography/film/maui.md new file mode 100644 index 0000000..b191f4d --- /dev/null +++ b/photography/film/maui.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "Maui" +permalink: /photography/film/maui/ +slug: maui +collection_type: film +author_profile: false +--- + +{% assign entry = site.data.photography.film | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="film" collection_title="35mm film" %} diff --git a/photography/film/pnw.md b/photography/film/pnw.md new file mode 100644 index 0000000..97b060a --- /dev/null +++ b/photography/film/pnw.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "PNW" +permalink: /photography/film/pnw/ +slug: pnw +collection_type: film +author_profile: false +--- + +{% assign entry = site.data.photography.film | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="film" collection_title="35mm film" %} diff --git a/photography/film/poland.md b/photography/film/poland.md new file mode 100644 index 0000000..db732b4 --- /dev/null +++ b/photography/film/poland.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "Poland" +permalink: /photography/film/poland/ +slug: poland +collection_type: film +author_profile: false +--- + +{% assign entry = site.data.photography.film | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="film" collection_title="35mm film" %} diff --git a/photography/film/rodchenko-inspiration.md b/photography/film/rodchenko-inspiration.md new file mode 100644 index 0000000..9061006 --- /dev/null +++ b/photography/film/rodchenko-inspiration.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "Rodchenko Inspiration" +permalink: /photography/film/rodchenko-inspiration/ +slug: rodchenko-inspiration +collection_type: film +author_profile: false +--- + +{% assign entry = site.data.photography.film | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="film" collection_title="35mm film" %} diff --git a/photography/film/ukraine.md b/photography/film/ukraine.md new file mode 100644 index 0000000..5f873d7 --- /dev/null +++ b/photography/film/ukraine.md @@ -0,0 +1,11 @@ +--- +layout: single +title: "Ukraine" +permalink: /photography/film/ukraine/ +slug: ukraine +collection_type: film +author_profile: false +--- + +{% assign entry = site.data.photography.film | where: "slug", page.slug | first %} +{% include photography/gallery.html entry=entry entry_type="film" collection_title="35mm film" %}