diff --git a/assets/css/custom.css b/assets/css/custom.css index 25a3f58..267ae82 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -165,4 +165,16 @@ video.mb-6 { article video { max-width: 100%; height: auto; +} + +/* Main page logo with programmatically generated sparks */ +body:has(.background-container) article.glass figure:first-of-type { + position: relative; + isolation: isolate; + display: inline-block; +} + +body:has(.background-container) article.glass figure:first-of-type img { + position: relative; + z-index: 0; } \ No newline at end of file diff --git a/assets/js/logo-sparks.js b/assets/js/logo-sparks.js new file mode 100644 index 0000000..1533442 --- /dev/null +++ b/assets/js/logo-sparks.js @@ -0,0 +1,118 @@ +// Programmatic spark generation for homepage logo +(function() { + const config = { + numSparks: 9, + minSize: 1.5, + maxSize: 3.5, + minRadius: 180, + maxRadius: 350, + minDuration: 23, + maxDuration: 49, + pathComplexity: 17, + behindLogoFraction: 0.75, + minOpacity: 0.5, + maxOpacity: 0.8, + staggerAnimations: true + }; + + function random(min, max) { + return Math.random() * (max - min) + min; + } + + function generatePath(radius, complexity) { + const points = []; + for (let i = 0; i <= complexity; i++) { + const angle = (i / complexity) * Math.PI * 2; + const r = random(radius * 0.8, radius * 1.2); + const x = Math.cos(angle) * r + random(-50, 50); + const y = Math.sin(angle) * r + random(-50, 50); + points.push({ x, y, scale: random(0.7, 1.3) }); + } + return points; + } + + function createKeyframes(id, path, isBehind) { + const steps = path.length; + let keyframes = '@keyframes spark' + id + ' {\n'; + + path.forEach((point, i) => { + const percent = (i / (steps - 1)) * 100; + const opacity = (i > 1 && i < steps - 2) ? random(config.minOpacity, config.maxOpacity) : 0; + + let opacityValue = opacity; + if (isBehind) { + const hiddenStart = random(30, 45); + const hiddenEnd = random(55, 70); + if (percent > hiddenStart && percent < hiddenEnd) { + opacityValue = 0; + } + } + + keyframes += ` ${percent.toFixed(1)}% {\n`; + keyframes += ` transform: translate(-50%, -50%) translateX(${point.x.toFixed(1)}px) translateY(${point.y.toFixed(1)}px) scale(${point.scale.toFixed(2)});\n`; + keyframes += ` opacity: ${opacityValue.toFixed(2)};\n`; + keyframes += ` }\n`; + }); + + keyframes += '}\n'; + return keyframes; + } + + function initSparks() { + const logoFigure = document.querySelector('body:has(.background-container) article.glass figure:first-of-type'); + if (!logoFigure) return; + + let allKeyframes = ''; + const sparks = []; + + for (let i = 0; i < config.numSparks; i++) { + const spark = document.createElement('span'); + spark.className = 'logo-spark'; + spark.dataset.sparkId = i; + + const size = random(config.minSize, config.maxSize); + const duration = random(config.minDuration, config.maxDuration); + const radius = random(config.minRadius, config.maxRadius); + const isBehind = Math.random() < config.behindLogoFraction; + + const delay = config.staggerAnimations + ? random(0, config.maxDuration) + : 0; + + spark.style.cssText = ` + position: absolute; + width: ${size}px; + height: ${size}px; + border-radius: 50%; + background: radial-gradient(circle, rgba(255, 255, 255, 0.9), rgba(74, 158, 255, 0.6), transparent); + box-shadow: 0 0 8px rgba(74, 158, 255, 0.8), 0 0 4px rgba(255, 255, 255, 0.6); + top: 50%; + left: 50%; + pointer-events: none; + z-index: ${isBehind ? -1 : 1}; + animation: spark${i} ${duration}s linear infinite; + animation-delay: -${delay.toFixed(2)}s; + `; + + const path = generatePath(radius, config.pathComplexity); + allKeyframes += createKeyframes(i, path, isBehind); + + sparks.push(spark); + } + + // Inject keyframes + const style = document.createElement('style'); + style.textContent = allKeyframes; + document.head.appendChild(style); + + // Inject sparks + sparks.forEach(spark => logoFigure.appendChild(spark)); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initSparks); + } else { + initSparks(); + } +})(); + diff --git a/content/professional-projects/usm-blueiq-acoustic-target-localization/index.md b/content/professional-projects/usm-blueiq-acoustic-target-localization/index.md index 782145f..c172903 100644 --- a/content/professional-projects/usm-blueiq-acoustic-target-localization/index.md +++ b/content/professional-projects/usm-blueiq-acoustic-target-localization/index.md @@ -7,9 +7,6 @@ categories = ['USM', 'BlueIQ', "Acoustics"] tags = ['data science', 'KarstTech', 'sonar', 'TDOA'] +++ -Using low-cost AI enabled acoustic buoys to detect and track vessels - - {{< katex >}} # Summary of "Enhancing Maritime Domain Awareness Through AI-Enabled Acoustic Buoys" diff --git a/content/professional-projects/usm-magnetics-machine-learning/index.md b/content/professional-projects/usm-magnetics-machine-learning/index.md index 379cd81..6b1a8ed 100644 --- a/content/professional-projects/usm-magnetics-machine-learning/index.md +++ b/content/professional-projects/usm-magnetics-machine-learning/index.md @@ -7,9 +7,6 @@ categories = ['USM', 'magnetics'] tags = ['data science', 'KarstTech', 'UUV', 'modeling', 'machine learning', 'AI'] +++ -Using machine learning to detect and localize magnetic targets underwater - - {{< katex >}} # Machine Learning for Target Classification and Localization diff --git a/content/professional-projects/usm-magnetics-magnetic-modeling/index.md b/content/professional-projects/usm-magnetics-magnetic-modeling/index.md index 1f499e7..99cefb7 100644 --- a/content/professional-projects/usm-magnetics-magnetic-modeling/index.md +++ b/content/professional-projects/usm-magnetics-magnetic-modeling/index.md @@ -7,9 +7,6 @@ categories = ['USM', 'magnetics'] tags = ['data science', 'KarstTech', 'UUV', 'modeling', 'machine learning', 'COMSOL'] +++ -Modeling magnetic targets to support machine learning applications in maritime environments - - # Potential Fields Modeling to Support Machine Learning Applications in Maritime Environments The summary below is from a published paper that I co-authored with my colleagues at USM Magnetics. If you would rather read the full paper, you can find it [here](https://www.comsol.com/paper/potential-fields-modeling-to-support-machine-learning-applications-in-maritime-environments-135922). diff --git a/content/professional-projects/usm-magnetics-magnetic-parametric-inversion/index.md b/content/professional-projects/usm-magnetics-magnetic-parametric-inversion/index.md index 110a563..bd2a072 100644 --- a/content/professional-projects/usm-magnetics-magnetic-parametric-inversion/index.md +++ b/content/professional-projects/usm-magnetics-magnetic-parametric-inversion/index.md @@ -7,9 +7,6 @@ categories = ['USM', 'magnetics'] tags = ['data science', 'KarstTech', 'modeling'] +++ -Estimating the position and magnetic moment of a dipole from a few field measurements - - {{< katex >}} # Magnetic Dipole Localization: Methods for Parametric Inversion diff --git a/content/professional-projects/usm-magnetics-sensor-fusion/index.md b/content/professional-projects/usm-magnetics-sensor-fusion/index.md index 88bc7a5..e6086c1 100644 --- a/content/professional-projects/usm-magnetics-sensor-fusion/index.md +++ b/content/professional-projects/usm-magnetics-sensor-fusion/index.md @@ -7,9 +7,6 @@ categories = ['USM', 'magnetics'] tags = ['data science', 'KarstTech', 'UUV', 'sensor fusion'] +++ -Combining data from multiple sensors to make them all more useful! - - ## Getting Started Several months ago I was referred to the University of Southern Mississippi [Marine Research Center](https://www.usm.edu/ocean-enterprise/marine-research-center.php) by a coworker. They wanted some assistance from someone with a physics and data science background to work on an autonomous underwater vehicle for the purpose of developing a magnetic sensing platform. diff --git a/layouts/partials/extend-head.html b/layouts/partials/extend-head.html new file mode 100644 index 0000000..e2fdb60 --- /dev/null +++ b/layouts/partials/extend-head.html @@ -0,0 +1,13 @@ +{{ if .IsHome }} + {{ $jsLogoSparks := resources.Get "js/logo-sparks.js" }} + {{ if $jsLogoSparks }} + {{ $jsLogoSparks = $jsLogoSparks | resources.Minify | resources.Fingerprint ($.Site.Params.fingerprintAlgorithm | default "sha256") }} + + {{ end }} +{{ end }} +