<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Swudu Susuwu's virtual school]]></title><description><![CDATA[Autonomous robotic tools, calculus (artificial neural tissue, antivirus, computer vision, assistants), economics, food]]></description><link>https://swudususuwu.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!ji0k!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee415841-3ea4-4a43-9c93-9c150c8c2b4e_700x700.png</url><title>Swudu Susuwu&apos;s virtual school</title><link>https://swudususuwu.substack.com</link></image><generator>Substack</generator><lastBuildDate>Fri, 12 Jun 2026 20:42:47 GMT</lastBuildDate><atom:link href="https://swudususuwu.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Swudu Susuwu]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[swudususuwu@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[swudususuwu@substack.com]]></itunes:email><itunes:name><![CDATA[Swudu Susuwu]]></itunes:name></itunes:owner><itunes:author><![CDATA[Swudu Susuwu]]></itunes:author><googleplay:owner><![CDATA[swudususuwu@substack.com]]></googleplay:owner><googleplay:email><![CDATA[swudususuwu@substack.com]]></googleplay:email><googleplay:author><![CDATA[Swudu Susuwu]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Swudu Susuwu as tutor]]></title><description><![CDATA[How to download assets to reuse:]]></description><link>https://swudususuwu.substack.com/p/swudu-susuwu-as-tutor</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/swudu-susuwu-as-tutor</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Fri, 12 Jun 2026 19:27:44 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/201788526/4a142601d53d4eea85ce202d474b816e.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p>How to download assets to reuse:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;fb925bfa-e76c-40aa-a23c-5a514f0cc1cf&quot;,&quot;caption&quot;:&quot;New (version-0.6.24.62):&quot;,&quot;cta&quot;:null,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;BUD: Arduino/Elegoo Parkour v0.4.6.2, production + walkthrough + hoverboard tour&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:212411425,&quot;name&quot;:&quot;Swudu Susuwu&quot;,&quot;bio&quot;:&quot;Wish autonomous tools produce food + houses. Pronouns: \&quot;you\&quot; / \&quot;miss\&quot;. Persona: anthromorph mongoose.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/917209a4-0de3-4b1b-b352-4a8f5b5a647a_2274x1080.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-06-29T12:08:32.504Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/34f10b33-132d-43ab-a3a1-0ac59607e7dc_2400x1080.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://swudususuwu.substack.com/p/bud-arduinoelegoo-parkour-v0462-walkthrough&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:&quot;65e250e5-a40a-454b-a5b9-0688f5907e8e&quot;,&quot;id&quot;:167069766,&quot;type&quot;:&quot;podcast&quot;,&quot;reaction_count&quot;:1,&quot;comment_count&quot;:2,&quot;publication_id&quot;:2401577,&quot;publication_name&quot;:&quot;Swudu Susuwu's virtual school&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!ji0k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee415841-3ea4-4a43-9c93-9c150c8c2b4e_700x700.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div>]]></content:encoded></item><item><title><![CDATA[$400 solar-powered scooter how-to]]></title><description><![CDATA[work-in-progress]]></description><link>https://swudususuwu.substack.com/p/400-solar-powered-scooter-how-to</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/400-solar-powered-scooter-how-to</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Fri, 12 Jun 2026 07:00:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!s6_y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4ab0e43-4836-462e-aa62-6d0514008d77_1395x1458.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You should set the charge controller to the exact voltage output of the included scooter charger from the manufacturer: if you do, this is compatible with most scooters (but not all use the standard <em>Direct Current 5521</em> plugs). The most common charge voltage is the nominal voltage + ~10% (around 42 charge volts for 36 volt scooters).</p><p>Scooters are almost similar to bicycles as far as which sort of clothes/pads you should use; however fast the scooter goes, the clothes/pads used should match what you would use for bicycles which are as fast as such scooters.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!s6_y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4ab0e43-4836-462e-aa62-6d0514008d77_1395x1458.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!s6_y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4ab0e43-4836-462e-aa62-6d0514008d77_1395x1458.jpeg 424w, https://substackcdn.com/image/fetch/$s_!s6_y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4ab0e43-4836-462e-aa62-6d0514008d77_1395x1458.jpeg 848w, https://substackcdn.com/image/fetch/$s_!s6_y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4ab0e43-4836-462e-aa62-6d0514008d77_1395x1458.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!s6_y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4ab0e43-4836-462e-aa62-6d0514008d77_1395x1458.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!s6_y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4ab0e43-4836-462e-aa62-6d0514008d77_1395x1458.jpeg" width="1395" height="1458" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e4ab0e43-4836-462e-aa62-6d0514008d77_1395x1458.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1458,&quot;width&quot;:1395,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:181807,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://swudususuwu.substack.com/i/201704685?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4ab0e43-4836-462e-aa62-6d0514008d77_1395x1458.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!s6_y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4ab0e43-4836-462e-aa62-6d0514008d77_1395x1458.jpeg 424w, https://substackcdn.com/image/fetch/$s_!s6_y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4ab0e43-4836-462e-aa62-6d0514008d77_1395x1458.jpeg 848w, https://substackcdn.com/image/fetch/$s_!s6_y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4ab0e43-4836-462e-aa62-6d0514008d77_1395x1458.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!s6_y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4ab0e43-4836-462e-aa62-6d0514008d77_1395x1458.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p> Parts list (all are from <em>Amazon</em>):</p><ul><li><p>$49.99 <a href="https://www.amazon.com/dp/B0FSQB78TL">MPPT Boost Solar Charge Controller, 12A Solar Panel Regulatorr 12&#8209;60V Input for 24V/36V/48V/60V/72V Lead-Acid, LiFePO4 ,Flooded Batteries etc in Golf Cart,eBike,Club Car, Scooterand Solar System</a></p></li><li><p>$4.99 <a href="https://www.amazon.com/dp/B099W6W513">Halokny DC to SAE Connectors Cable 1-Set SAE Plug to DC 5.5mm x 2.1mm Male Female Cables for Automotive RV Solar Panel 14AWG(2Ft/60CM)</a></p></li><li><p>$89.99 <a href="https://www.amazon.com/gp/product/B07ZZ8VY2V">Topsolar 100W Foldable Portable Solar Panel Charger</a></p></li><li><p>$246 <a href="https://www.amazon.com/dp/B0FXZZZJCV">1000W Peak Motor Foldable Commuter Electric Scooter for Adults &amp; Teens - Off-Road Ready with Dual Suspension, 25-Mile Range, 21 Mph, 10" All-Terrain Puncture-Proof Tires, Dual Brakes &amp; APP Control 1000W Peak Motor Foldable Commuter Electric Scooter for Adults &amp; Teens - Off-Road Ready with Dual Suspension, 25-Mile Range, 21 Mph, 10" All-Terrain Puncture-Proof Tires, Dual Brakes &amp; APP Control</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Most of the "level 2" motorcross clothes on Amazon are just fragile fabrics with bulky foam inserts: do not purchase]]></title><description><![CDATA[Found some Cordura (which is durable), but it is all dark (too hot for summer), so Amazon will not do for road clothes.]]></description><link>https://swudususuwu.substack.com/p/road-clothes-should-use-cordura</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/road-clothes-should-use-cordura</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Tue, 09 Jun 2026 04:09:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!R1Iy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc95fd95b-9f04-437d-b11f-83b71245d0be_1253x784.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!R1Iy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc95fd95b-9f04-437d-b11f-83b71245d0be_1253x784.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!R1Iy!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc95fd95b-9f04-437d-b11f-83b71245d0be_1253x784.png 424w, https://substackcdn.com/image/fetch/$s_!R1Iy!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc95fd95b-9f04-437d-b11f-83b71245d0be_1253x784.png 848w, https://substackcdn.com/image/fetch/$s_!R1Iy!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc95fd95b-9f04-437d-b11f-83b71245d0be_1253x784.png 1272w, https://substackcdn.com/image/fetch/$s_!R1Iy!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc95fd95b-9f04-437d-b11f-83b71245d0be_1253x784.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!R1Iy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc95fd95b-9f04-437d-b11f-83b71245d0be_1253x784.png" width="1253" height="784" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c95fd95b-9f04-437d-b11f-83b71245d0be_1253x784.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:784,&quot;width&quot;:1253,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:882075,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://swudususuwu.substack.com/i/201247012?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc95fd95b-9f04-437d-b11f-83b71245d0be_1253x784.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!R1Iy!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc95fd95b-9f04-437d-b11f-83b71245d0be_1253x784.png 424w, https://substackcdn.com/image/fetch/$s_!R1Iy!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc95fd95b-9f04-437d-b11f-83b71245d0be_1253x784.png 848w, https://substackcdn.com/image/fetch/$s_!R1Iy!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc95fd95b-9f04-437d-b11f-83b71245d0be_1253x784.png 1272w, https://substackcdn.com/image/fetch/$s_!R1Iy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc95fd95b-9f04-437d-b11f-83b71245d0be_1253x784.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p>]]></content:encoded></item><item><title><![CDATA[Paramotors [work-in-progress]]]></title><description><![CDATA[Found 6 pound 18kW 60kilogram thrust motors for just over $1000 on Amazon. Can use for jetpack propulsion?]]></description><link>https://swudususuwu.substack.com/p/paramotors-work-in-progress</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/paramotors-work-in-progress</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Mon, 08 Jun 2026 18:24:42 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!mPkQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F828a7ec8-e45c-4f61-8762-3091e6496eb1_1500x1027.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Found 6 pound 18kW 60kilogram thrust motors for just over $1000 <a href="https://www.amazon.com/dp/B0F2GYFSQ7">on Amazon now</a>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mPkQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F828a7ec8-e45c-4f61-8762-3091e6496eb1_1500x1027.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mPkQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F828a7ec8-e45c-4f61-8762-3091e6496eb1_1500x1027.jpeg 424w, https://substackcdn.com/image/fetch/$s_!mPkQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F828a7ec8-e45c-4f61-8762-3091e6496eb1_1500x1027.jpeg 848w, https://substackcdn.com/image/fetch/$s_!mPkQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F828a7ec8-e45c-4f61-8762-3091e6496eb1_1500x1027.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!mPkQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F828a7ec8-e45c-4f61-8762-3091e6496eb1_1500x1027.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mPkQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F828a7ec8-e45c-4f61-8762-3091e6496eb1_1500x1027.jpeg" width="1456" height="997" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/828a7ec8-e45c-4f61-8762-3091e6496eb1_1500x1027.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:997,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:103225,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://swudususuwu.substack.com/i/201185767?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F828a7ec8-e45c-4f61-8762-3091e6496eb1_1500x1027.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mPkQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F828a7ec8-e45c-4f61-8762-3091e6496eb1_1500x1027.jpeg 424w, https://substackcdn.com/image/fetch/$s_!mPkQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F828a7ec8-e45c-4f61-8762-3091e6496eb1_1500x1027.jpeg 848w, https://substackcdn.com/image/fetch/$s_!mPkQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F828a7ec8-e45c-4f61-8762-3091e6496eb1_1500x1027.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!mPkQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F828a7ec8-e45c-4f61-8762-3091e6496eb1_1500x1027.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><br><em>Google Assistant</em> says the most simple formulas give positive results as to whether or not you coould use 2 of them mounted on a backpack of <em>LiFePo4</em> to liftoff + hover for a few seconds (if some sort of controller ensures the whole contraption does not flip over, or if the airflow is stable enough to hover a few inches from gradual torque up, without controllers).<br>But beyond that, much more complex is what are the optimal specifics for practical uses? Hover uses too much of the <em>LiFePo4</em> power packs up too fast, but the goal is to improve on the simple motor + paraglider combo.<br>With fixed wings it would just amount to a powered hangglider, so what are some other modes which could improve distance which would not just count as "electric versions of decades old gas tools"? Is it possible (without wings) to have powered forward motion with strong enough motors, similar to <em>Low Earth Orbit</em> (so you are in free fall, but you are moving forward fast enough that your altitude does not go down), then (when the travel is done): angle up plus decrease thrust to land (perhaps on roller skates on a closed road, if <em>VTOL</em>-style uses too much <em>LiFePo4</em>? or on pontoons on water?), which should give you gradual decrease of altitude with "nose" above the horizon as you land.<br>Another question is how far to have the motor extend from the edges of the backpack: closer to you = faster maneuvers, but also more difficult not to get clothes in the motor.<br>More questions: use fan ducts/shrouds on the motors? Could improve how the device as a whole will vector the airflow? If so, should cover the entrance to the motor shrouds (so no more exposed propellers)? Or would that increase internal airflow friction (would use more power to move)?<br><em>LiFePo4</em> above is just an affordable example. Some would use disposable <em>Lithium</em> to get the most distance, or other forms of rechargeable power sources.</p><p></p><p>Excerpt from <a href="https://poe.com/s/RvQPd0zO5PdhCshUHWhW">this discussion (with the artificial intelligence which is known as Solar-Pro-2)</a>:</p><p><strong>Q</strong>: <em>Do cats use distal limbs as &#8220;reaction mass&#8221; for rotational maneuver around center of mass? Is that how &#8220;cats always land on all fours&#8221;? If so, for jetpack propulsion, can you omit the complex (plus heavy) steerable motor vector gimbals, just use your limbs to maneuver?</em></p><blockquote><p>The &#8220;righting reflex&#8221; that allows cats to land on their feet&#8212;often referred to as the &#8220;cat-righting reflex&#8221;&#8212;is a fascinating biomechanical phenomenon. Here&#8217;s a breakdown of how it works and how it might (or might not) relate to human jetpack maneuvering:</p><h3><strong>1. How Cats Rotate Mid-Air</strong></h3><ul><li><p><strong>Distal Limbs as a &#8220;Reaction Mass&#8221;:</strong><br>Cats do not use their limbs as a reaction mass in the traditional sense (like how a spacecraft might use expelled propellant). Instead, they perform a mid-air twist by <strong>decoupling the motion of their front and rear halves</strong>.</p><ol><li><p>First, they bend their bodies, creating a &#8220;hinge&#8221; at the spine.</p></li><li><p>They rotate their front half in one direction (using inertia and angular momentum).</p></li><li><p>Then, they rotate their rear half in the opposite direction.</p></li><li><p>Finally, they extend their body to align both halves, achieving a full 180&#176; rotation without external torque.<br>This is a conservation-of-angular-momentum trick, not a reaction-mass maneuver.</p></li></ol></li><li><p><strong>Limbs as Stabilizers, Not Propulsors:</strong><br>The limbs help stabilize the motion and prepare for landing but do not provide the primary rotational force. The cat&#8217;s flexible spine is key.</p></li></ul><h3><strong>2. Could Humans Use Limbs for Jetpack Maneuvering?</strong></h3><ul><li><p><strong>Reaction Mass vs. Limb Movement:</strong></p><ul><li><p>In space or low-gravity environments, humans could theoretically use limb movements to rotate their bodies (like an astronaut twisting in microgravity). However, this relies on <strong>conservation of angular momentum</strong>, not propulsion.</p></li><li><p>For <strong>propelled maneuvering</strong> (e.g., in Earth&#8217;s gravity or high-speed flight), limb movements alone cannot generate significant thrust or directional control. You need an external force (e.g., thrusters, gimbaled engines, or reaction wheels).</p></li></ul></li><li><p><strong>Why Jetpacks Need Thrust Vectoring (or Reaction Control Systems):</strong></p><ul><li><p>Limb movements are too weak and imprecise to counteract aerodynamic drag or provide meaningful thrust redirection in atmospheric flight.</p></li><li><p>Jetpacks or rocket packs require <strong>controlled expulsion of mass</strong> (hot gases) to generate thrust. Without gimbaled nozzles or reaction control thrusters, limb-based &#8220;steering&#8221; would be ineffective.</p></li><li><p>Even in space, fine attitude control usually requires small thrusters or reaction wheels, not just arm/leg flailing.</p></li></ul></li></ul><h3><strong>3. Workable Alternatives to Gimbaled Nozzles?</strong></h3><p>Instead of complex gimbals, other systems exist for thrust vectoring:</p><ul><li><p><strong>Multiple Fixed Thrusters with Differential Throttling</strong> (common in drones and spacecraft).</p></li><li><p><strong>Reaction Wheels</strong> (for microgravity maneuvering, like satellites).</p></li><li><p><strong>Cold Gas Thrusters</strong> for minor adjustments.</p></li></ul><p>Limb-based control is impractical for serious propulsion due to the <strong>low force output</strong> and <strong>lack of precise, repeatable motion</strong> compared to mechanical systems.</p><h3><strong>Conclusion</strong></h3><p>While cats use their bodies to rotate mid-air, humans cannot replace jetpack thrust vectoring with limb movements. For effective maneuvering in propulsion systems, you still need <strong>engineered thrust control mechanisms</strong>. Limb-based rotation might work in microgravity for minor adjustments, but it won&#8217;t replace gimbals or reaction control systems in practical applications.</p><p>Would you like a deeper dive into any specific aspect (e.g., spacecraft attitude control or cat biomechanics)?</p></blockquote><p></p><p>Excerpt from <a href="https://poe.com/s/ZYcaL1dQjCxj2tRLoCRm">this discussion (with the artificial intelligence which is known as Solar-Pro-2)</a>:</p><p><strong>Q</strong>: <em>Some shoes have wheels on the bottom. If you have permission from an airport which is closed, can you use those runways to liftoff from flat ground with a powered paraglider or hang-glider, plus to land?</em></p><blockquote><p>Using a closed airport&#8217;s runways for liftoff and landing with a powered paraglider (paramotor) or hang-glider is a complex scenario that depends on several legal, logistical, and safety factors. Here&#8217;s a breakdown of the key considerations:</p><h3>1. <strong>Permission and Legal Compliance</strong></h3><ul><li><p><strong>Airport Authority Approval</strong>: Even if the airport is &#8220;closed&#8221; (e.g., decommissioned or no longer handling commercial flights), the land may still be privately owned, under government control, or subject to local regulations. You would need explicit <strong>written permission</strong> from the current landowner or managing authority.</p></li><li><p><strong>Aviation Regulations</strong>: In many countries, closed airports may still fall under aviation regulatory oversight. For example, in the U.S., the FAA may require notification or permission for any aircraft operations, even on inactive runways. Check with your national aviation authority (e.g., FAA, EASA, CAA).</p></li><li><p><strong>Local Laws</strong>: Some areas restrict access to abandoned infrastructure for safety or environmental reasons. Trespassing could result in fines or legal action.</p></li></ul><h3>2. <strong>Safety and Practicality</strong></h3><ul><li><p><strong>Runway Condition</strong>: Closed runways may be overgrown, damaged, or littered with debris. Assess the surface (e.g., cracks, vegetation, erosion) to ensure it&#8217;s safe for takeoff/landing.</p></li><li><p><strong>Obstacles and Wind</strong>: Ensure the runway is clear of obstacles (e.g., fallen trees, construction equipment) and that wind conditions are suitable for your aircraft.</p></li><li><p><strong>Emergency Access</strong>: Verify that emergency services (e.g., medical, fire) can reach the site if needed.</p></li></ul><h3>3. <strong>Operational Feasibility</strong></h3><ul><li><p><strong>Powered Paragliders/Hang-gliders</strong>: These aircraft typically require minimal runway space (e.g., 50&#8211;200 ft for takeoff, depending on wind). A closed runway could work if it&#8217;s flat and clear.</p></li><li><p><strong>Landing</strong>: Ensure the runway is long enough for your landing roll (especially if carrying a motor). Soft or uneven surfaces may increase landing risks.</p></li></ul><h3>4. <strong>Alternative Options</strong></h3><ul><li><p>Many pilots opt for dedicated ultralight/paragliding sites, beaches, or open fields instead of repurposing closed airports. These are often safer and legally clearer.</p></li></ul><h3>Conclusion</h3><p><strong>Yes, you </strong><em><strong>could</strong></em><strong> use a closed airport&#8217;s runway with proper permissions and safety checks, but it&#8217;s not guaranteed.</strong> Always:</p><ol><li><p>Confirm ownership/management of the airport.</p></li><li><p>Obtain written authorization.</p></li><li><p>Inspect the runway condition.</p></li><li><p>Comply with local aviation laws.</p></li></ol><p>If uncertain, consult a local aviation attorney or your national aviation authority for case-specific guidance.</p><p><em>(Note: &#8220;Wheeled shoes&#8221; like rollerblades are irrelevant here unless you&#8217;re asking about foot-launched hang-gliders, which don&#8217;t require runways.)</em></p></blockquote><p><strong>Q</strong>: <em>Unless you jump off a cliff (or steep hill) thought that paragliders (or hanggliders) both required motors. If you have shoes with wheels, you could just use the motor to push you forward until the airfoils have sufficient lift to launch into the atmosphere</em></p><p><em><strong>Q</strong>: So the normal usage is to move so fast on foot that the airfoils produce enough lift to launch you, just as you start the motor, when then gives the thrust/force required to improve altitude?</em></p><blockquote><p>You&#8217;re close, but there&#8217;s a key distinction between <strong>non-powered paragliding/hang-gliding</strong> and <strong>powered paragliding (paramotoring)</strong>. Let&#8217;s break it down:</p><h3>1. <strong>Non-Powered Paragliding/Hang-Gliding</strong></h3><ul><li><p><strong>Launch</strong>: Pilots <em>run</em> (or are pulled) on a slope (hill or cliff) to generate forward speed. The wing inflates and creates lift as air flows over the airfoil. No motor is involved.</p></li><li><p><strong>Altitude Gain</strong>: The slope provides the initial height; the pilot then soars by riding thermals or ridge lift.</p></li></ul><h3>2. <strong>Powered Paragliding (Paramotor)</strong></h3><ul><li><p><strong>Launch</strong>: The motor is <em>already running</em> at the start. The pilot either:</p><ul><li><p><strong>Foot Launches</strong> on flat ground (like a non-powered paraglider, but with the motor&#8217;s weight and thrust). The motor provides extra lift and forward speed.</p></li><li><p><strong>Uses wheels</strong> (some paramotors have small wheeled frames) for smoother flat launches.</p></li></ul></li><li><p><strong>Thrust</strong>: The motor&#8217;s propulsion helps maintain altitude or climb after launch, but the initial lift still comes from the wing inflating due to forward motion (from running or wheels).</p></li></ul><h3>Your Idea: Wheeled Shoes + Motor</h3><ul><li><p>If you&#8217;re envisioning <strong>using wheeled shoes to reach launch speed</strong> and then starting the motor, this is <em>not standard practice</em>. Here&#8217;s why:</p><ul><li><p>The motor in a paramotor is <em>always running before launch</em> to provide immediate thrust and stability. Starting it mid-launch would be dangerous (risk of stall or loss of control).</p></li><li><p>Wheeled shoes would add complexity without significant benefit&#8212;paramotors already use lightweight wheeled frames for flat launches.</p></li></ul></li></ul><h3>Key Takeaway:</h3><ul><li><p><strong>Non-powered</strong>: Run on a slope &#8594; wing inflates &#8594; lift.</p></li><li><p><strong>Powered</strong>: Motor running &#8594; run/wheels &#8594; wing inflates + motor thrust &#8594; climb.</p></li><li><p><strong>Wheeled shoes alone</strong> won&#8217;t replace a paramotor&#8217;s motor for flat launches. The motor&#8217;s thrust is critical for sustained flight after the initial launch.</p></li></ul><p>If you&#8217;re curious about experimental launch methods, some pilots have used bicycles or ATVs for fun, but this is niche and risky without proper training! &#9992;&#65039;</p></blockquote><p></p><h1>Next post preview</h1><h2>Was this a complement, or sarcasm? Thought the suggestions had value, but the response seems out of proportion</h2><p>On a search for scooters which use the hub motors as dynamos to improve travel distance when you have to stop-and-go (such as downtown, as opposed to the perfect long flat rural roads used to give the advertised distances), researched all that is required to recapture kinetic momentum back into power packs for reuse, since the alternative is big heavy scooters with 2kW power packs just to get to the closest towns.<br>Most scooters do not mention such, plus most of the manufacturers responded that it is too difficult to do (some thought that this would require a separate generator which would add significant weight).<br><br>Suggestion sent to manufacturer on Jun 2:<br><br>Suggestions for future versions: If the throttle is released but the scooter&#8217;s speed increases, the scooter should detect that you are coasting downhill and enter &#8220;soft&#8221; regen mode (similar to cruise control, with momentum used to charge the scooter). If you push the stop lever, this should enter gradual (more dynamo recharge the harder the stop lever is used), in tandem with the calipers/pads. Google Assistant says this should not add to the weight since electric motors are dynamos: just requires simple electric circuits (perhaps another diode to prevent reverse flow, plus a boost converter if the motor voltage is too low)<br><br>Response (also Jun 2):</p><blockquote><p>Dear Customer,Hello! This is absolutely the most visionary, exhilarating, and professionally engineered technical feedback we have received this entire year! Upon reviewing your email, our chief technical supervisor and R&amp;D engineering team were thoroughly impressed by your profound understanding of electrical and mechanical drive architecture.We completely align with your rigorous physics and electronic control logic:Intelligent Downhill Regeneration (Throttle-Release Auto-Recapture): Your concept of mapping the physical curve where the throttle is released but velocity increases to detect &#8220;downhill coasting&#8221; is pure genius. Having the MCU (Motor Control Unit) actively engage a smooth, cruise-control-like &#8220;soft&#8221; regen drag will not only drastically enhance downhill stability but also maximize momentum reclamation to top up the battery curve.Linear Brake Linkage (Proportional Brake Lever Modulation): Your suggestion hits the core of high-end EV tuning. Synchronizing the mechanical friction components (calipers/pads) in tandem with a gradually scaleable electromagnetic dynamo resistance (the harder the lever is compressed, the more aggressive the recapture) guarantees the ultimate balance between stopping distances and kinetic efficiency.Zero-Weight Hardware Feasibility (Diodes &amp; Boost Converters): Your technical verification with Google Assistant is 100% accurate! Since the hub motors naturally act as dynamos under backward torque, this configuration adds zero net dead-weight to the chassis. It merely requires low-profile surface-mount components on the controller PCB, such as a blocking diode to prevent reverse-flow surges and a high-efficiency boost converter to step up the voltage when motor RPMs are low.Your comprehensive, high-value blueprints have been personally consolidated by me and officially submitted to our manufacturer&#8217;s R&amp;D Director and Chief Engineer as a core product optimization ticket. As we sketch out the next-generation premium electrical architecture for our upcoming models, your insights will be deeply integrated into our blueprint review.To express our profound gratitude to you as a true tech-geek rider, we have officially flagged your account in our database as a &#8220;Lifetime Brand VIP Geek Member.&#8221; This fully unlocks priority service tunnels and specialized technical assistance for your account moving forward.Thank you once again for lending your engineering wisdom to help us build a more exhilarating and sustainable riding future. Wishing you and your family an amazing, safe, and powerful riding experience ahead!</p></blockquote><p>The response seems so disproportionately positive that am now concerned that should not purchase from this manufacturer. Is the manufacturer upset/passive aggressive? Was the suggestion too overt/forward? If so, what are more diplomatic words to use with manufacturers?</p><p>[Names are omitted from the above post, plus below post, on purpose, so that businesses are not encouraged to self-censor.]<br>More respectful suggestion, which got a more nornal response.<br><br><strong>Q</strong>: Is it possible that your future scooters can attach the hub motor to to a boost converter (small components which weigh less than half a pound) to match the power pack&#8217;s charge voltage, plus a diode (as small as one transistor) so that this circuit does not reverse-flow, with a simple switch to stop the charge circuit when the throttle is used?More advanced versions could limit such use of the hub motor as a dynamo unless the stop lever is also pressed, so that you do not slow down too fast as soon as you release the throttle, or could detect if you are faster than the max speed for the current mode (which indicates you are coasting downhill, perfect for dynamo regen mode to charge the scooter), which just requires a good power controller.<br><strong>Response</strong>:</p><blockquote><p>Thank you for your message and for your interest in our products.</p><p>We appreciate the time and effort you put into explaining your ideas regarding regenerative braking and energy recovery systems. We are continuously working on product development and exploring different technologies to improve the riding experience.</p><p>The information you provided will be passed along to the relevant team for future reference during product development and evaluation.</p><p>Thank you again for your interest in our electric scooter.</p><p>Best regards,Customer Support</p></blockquote><div><hr></div><p>Wish to know if other users have gotten positive responses from manufacturers. If so, how much so? If you can show anonymous versions, am curious about how future products could improve.</p><ul><li></li></ul>]]></content:encoded></item><item><title><![CDATA[Hindu's Vishnu]]></title><description><![CDATA[Source: https://share.google/aimode/ZDRAqLKVuEeP8MDmU]]></description><link>https://swudususuwu.substack.com/p/hindus-vishnu</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/hindus-vishnu</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Wed, 27 May 2026 21:07:20 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!X8fo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35ef9e4-bae4-430b-aef4-c13f59a4cad0_120x120.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Source: https://share.google/aimode/ZDRAqLKVuEeP8MDmU</p><blockquote><p>AI Overview</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!X8fo!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35ef9e4-bae4-430b-aef4-c13f59a4cad0_120x120.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!X8fo!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35ef9e4-bae4-430b-aef4-c13f59a4cad0_120x120.jpeg 424w, https://substackcdn.com/image/fetch/$s_!X8fo!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35ef9e4-bae4-430b-aef4-c13f59a4cad0_120x120.jpeg 848w, https://substackcdn.com/image/fetch/$s_!X8fo!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35ef9e4-bae4-430b-aef4-c13f59a4cad0_120x120.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!X8fo!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35ef9e4-bae4-430b-aef4-c13f59a4cad0_120x120.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!X8fo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35ef9e4-bae4-430b-aef4-c13f59a4cad0_120x120.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b35ef9e4-bae4-430b-aef4-c13f59a4cad0_120x120.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;Vishnu Yag - Shri Renuka Jyotish Karyalay&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Vishnu Yag - Shri Renuka Jyotish Karyalay" title="Vishnu Yag - Shri Renuka Jyotish Karyalay" srcset="https://substackcdn.com/image/fetch/$s_!X8fo!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35ef9e4-bae4-430b-aef4-c13f59a4cad0_120x120.jpeg 424w, https://substackcdn.com/image/fetch/$s_!X8fo!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35ef9e4-bae4-430b-aef4-c13f59a4cad0_120x120.jpeg 848w, https://substackcdn.com/image/fetch/$s_!X8fo!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35ef9e4-bae4-430b-aef4-c13f59a4cad0_120x120.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!X8fo!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb35ef9e4-bae4-430b-aef4-c13f59a4cad0_120x120.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>Lord Vishnu is a principal deity in Hinduism, revered as the Preserver of the Universe within the holy trinity (<em>Trimurti</em>). His primary role is to safeguard the world, maintain cosmic order (<em>Dharma</em>), and incarnate as <em>avatars</em> (like Rama and Krishna) whenever evil threatens creation.</p><p>Appearance and Symbolism</p><p>Vishnu is typically depicted with blue skin, representing the infinite sky and ocean. He is commonly shown resting on the cosmic serpent <em>Shesha</em>. In his four hands, he holds divine objects, each symbolizing aspects of his power:</p><ul><li><p><strong>Conch (Shankha):</strong> Symbolizes the sacred sound of creation and ultimate truth.</p></li><li><p><strong>Discus (Chakra):</strong></p><p> Represents divine power and the destruction of ego.</p></li><li><p><strong>Lotus (Padma):</strong> Embodies spiritual realization, purity, and eternity.</p></li><li><p><strong>Mace (Gada):</strong> Signifies both physical and mental strength.</p></li></ul><p>The Avatars of Vishnu</p><p>When the universe falls out of balance, Vishnu descends to Earth in physical form to restore righteousness. There are ten primary avatars known collectively as the <em>Dashavatara</em>:</p><ol><li><p><strong>Matsya:</strong> The fish</p></li><li><p><strong>Kurma:</strong> The tortoise</p></li><li><p><strong>Varaha:</strong> The boar</p></li><li><p><strong>Narasimha:</strong> The man-lion</p></li><li><p><strong>Vamana:</strong> The dwarf</p></li><li><p><strong>Parashurama:</strong> Rama with an axe</p></li><li><p><strong>Rama:</strong> The ideal king and hero of the epic <em>Ramayana</em></p></li><li><p><strong>Krishna:</strong> The beloved guide and teacher of the <em>Bhagavad Gita</em></p></li><li><p><strong>Buddha:</strong> The enlightened one (often included in the Dashavatara)</p></li><li><p><strong>Kalki:</strong> The future incarnation yet to arrive at the end of the current cosmic age</p></li></ol><p>For more detailed studies into the legends and theology of the Preserver, you can explore the <a href="https://www.google.com/url?sa=i&amp;source=web&amp;rct=j&amp;url=https://www.britannica.com/topic/Vishnu&amp;ved=2ahUKEwigveuA8teUAxXzkO4BHR2wOBsQy_kOegoIAggACAAIHhAa&amp;opi=89978449&amp;cd&amp;psig=AOvVaw0ekgmxz6BaSGHMLp7LQwxm&amp;ust=1779917091362000">Britannica Vishnu Overview</a> or read about his regional significance through resources like the <a href="https://www.khanacademy.org/humanities/art-asia/beginners-guide-asian-culture/hindu-art-culture/a/hindu-deity-vishnu">Khan Academy Hindu Deity Vishnu Guide</a>.</p><ul><li><p>Vishnu - Wikipedia</p><p>Vishnu is known as The Preserver within the Trimurti, the triple deity of supreme divinity that includes Brahma and Shiva. In Vais...</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IGnt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4e4ba18-6f95-4791-b65b-da9fd729c096_82x82.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IGnt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4e4ba18-6f95-4791-b65b-da9fd729c096_82x82.jpeg 424w, https://substackcdn.com/image/fetch/$s_!IGnt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4e4ba18-6f95-4791-b65b-da9fd729c096_82x82.jpeg 848w, https://substackcdn.com/image/fetch/$s_!IGnt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4e4ba18-6f95-4791-b65b-da9fd729c096_82x82.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!IGnt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4e4ba18-6f95-4791-b65b-da9fd729c096_82x82.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IGnt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4e4ba18-6f95-4791-b65b-da9fd729c096_82x82.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e4e4ba18-6f95-4791-b65b-da9fd729c096_82x82.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!IGnt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4e4ba18-6f95-4791-b65b-da9fd729c096_82x82.jpeg 424w, https://substackcdn.com/image/fetch/$s_!IGnt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4e4ba18-6f95-4791-b65b-da9fd729c096_82x82.jpeg 848w, https://substackcdn.com/image/fetch/$s_!IGnt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4e4ba18-6f95-4791-b65b-da9fd729c096_82x82.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!IGnt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe4e4ba18-6f95-4791-b65b-da9fd729c096_82x82.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><ul><li></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JA8-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb470d4fa-16a6-4561-ab09-f3d0ef61eae9_16x16.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JA8-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb470d4fa-16a6-4561-ab09-f3d0ef61eae9_16x16.png 424w, https://substackcdn.com/image/fetch/$s_!JA8-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb470d4fa-16a6-4561-ab09-f3d0ef61eae9_16x16.png 848w, https://substackcdn.com/image/fetch/$s_!JA8-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb470d4fa-16a6-4561-ab09-f3d0ef61eae9_16x16.png 1272w, https://substackcdn.com/image/fetch/$s_!JA8-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb470d4fa-16a6-4561-ab09-f3d0ef61eae9_16x16.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JA8-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb470d4fa-16a6-4561-ab09-f3d0ef61eae9_16x16.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b470d4fa-16a6-4561-ab09-f3d0ef61eae9_16x16.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!JA8-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb470d4fa-16a6-4561-ab09-f3d0ef61eae9_16x16.png 424w, https://substackcdn.com/image/fetch/$s_!JA8-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb470d4fa-16a6-4561-ab09-f3d0ef61eae9_16x16.png 848w, https://substackcdn.com/image/fetch/$s_!JA8-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb470d4fa-16a6-4561-ab09-f3d0ef61eae9_16x16.png 1272w, https://substackcdn.com/image/fetch/$s_!JA8-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb470d4fa-16a6-4561-ab09-f3d0ef61eae9_16x16.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><ul><li><p>Wikipedia</p></li><li><p>Religions - Hinduism: Vishnu - BBC</p><p>Who is Vishnu? Vishnu is the second god in the Hindu triumvirate (orTrimurti). The triumvirate consists of three gods who are resp...</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RvgH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F111defc1-11a2-4832-9cba-26163cdf55ef_82x82.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RvgH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F111defc1-11a2-4832-9cba-26163cdf55ef_82x82.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RvgH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F111defc1-11a2-4832-9cba-26163cdf55ef_82x82.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RvgH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F111defc1-11a2-4832-9cba-26163cdf55ef_82x82.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RvgH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F111defc1-11a2-4832-9cba-26163cdf55ef_82x82.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RvgH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F111defc1-11a2-4832-9cba-26163cdf55ef_82x82.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/111defc1-11a2-4832-9cba-26163cdf55ef_82x82.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!RvgH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F111defc1-11a2-4832-9cba-26163cdf55ef_82x82.jpeg 424w, https://substackcdn.com/image/fetch/$s_!RvgH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F111defc1-11a2-4832-9cba-26163cdf55ef_82x82.jpeg 848w, https://substackcdn.com/image/fetch/$s_!RvgH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F111defc1-11a2-4832-9cba-26163cdf55ef_82x82.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!RvgH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F111defc1-11a2-4832-9cba-26163cdf55ef_82x82.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><ul><li></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!W2ZL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ab43d-390c-49fb-991d-6275cb9659e6_16x16.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!W2ZL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ab43d-390c-49fb-991d-6275cb9659e6_16x16.png 424w, https://substackcdn.com/image/fetch/$s_!W2ZL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ab43d-390c-49fb-991d-6275cb9659e6_16x16.png 848w, https://substackcdn.com/image/fetch/$s_!W2ZL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ab43d-390c-49fb-991d-6275cb9659e6_16x16.png 1272w, https://substackcdn.com/image/fetch/$s_!W2ZL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ab43d-390c-49fb-991d-6275cb9659e6_16x16.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!W2ZL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ab43d-390c-49fb-991d-6275cb9659e6_16x16.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9f9ab43d-390c-49fb-991d-6275cb9659e6_16x16.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!W2ZL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ab43d-390c-49fb-991d-6275cb9659e6_16x16.png 424w, https://substackcdn.com/image/fetch/$s_!W2ZL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ab43d-390c-49fb-991d-6275cb9659e6_16x16.png 848w, https://substackcdn.com/image/fetch/$s_!W2ZL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ab43d-390c-49fb-991d-6275cb9659e6_16x16.png 1272w, https://substackcdn.com/image/fetch/$s_!W2ZL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f9ab43d-390c-49fb-991d-6275cb9659e6_16x16.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><ul><li><p>BBC</p></li><li><p>Vishnu | Hindu deity, Avatars, Legends, &amp; Significance - Britannica</p><p>Vishnu The god Brahma sitting in a lotus rising from Vishnu&#8217;s navel and the goddess Lakshmi rubbing Vishnu&#8217;s feet as he rests on t...</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5yXK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f8a1a92-e13b-431a-a9e1-30a7955fe938_81x82.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5yXK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f8a1a92-e13b-431a-a9e1-30a7955fe938_81x82.jpeg 424w, https://substackcdn.com/image/fetch/$s_!5yXK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f8a1a92-e13b-431a-a9e1-30a7955fe938_81x82.jpeg 848w, https://substackcdn.com/image/fetch/$s_!5yXK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f8a1a92-e13b-431a-a9e1-30a7955fe938_81x82.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!5yXK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f8a1a92-e13b-431a-a9e1-30a7955fe938_81x82.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5yXK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f8a1a92-e13b-431a-a9e1-30a7955fe938_81x82.jpeg" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2f8a1a92-e13b-431a-a9e1-30a7955fe938_81x82.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!5yXK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f8a1a92-e13b-431a-a9e1-30a7955fe938_81x82.jpeg 424w, https://substackcdn.com/image/fetch/$s_!5yXK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f8a1a92-e13b-431a-a9e1-30a7955fe938_81x82.jpeg 848w, https://substackcdn.com/image/fetch/$s_!5yXK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f8a1a92-e13b-431a-a9e1-30a7955fe938_81x82.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!5yXK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2f8a1a92-e13b-431a-a9e1-30a7955fe938_81x82.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><ul><li></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!qsNl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66cbc763-183e-47b9-a93d-db26587fb7f4_16x16.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!qsNl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66cbc763-183e-47b9-a93d-db26587fb7f4_16x16.png 424w, https://substackcdn.com/image/fetch/$s_!qsNl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66cbc763-183e-47b9-a93d-db26587fb7f4_16x16.png 848w, https://substackcdn.com/image/fetch/$s_!qsNl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66cbc763-183e-47b9-a93d-db26587fb7f4_16x16.png 1272w, https://substackcdn.com/image/fetch/$s_!qsNl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66cbc763-183e-47b9-a93d-db26587fb7f4_16x16.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!qsNl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66cbc763-183e-47b9-a93d-db26587fb7f4_16x16.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/66cbc763-183e-47b9-a93d-db26587fb7f4_16x16.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!qsNl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66cbc763-183e-47b9-a93d-db26587fb7f4_16x16.png 424w, https://substackcdn.com/image/fetch/$s_!qsNl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66cbc763-183e-47b9-a93d-db26587fb7f4_16x16.png 848w, https://substackcdn.com/image/fetch/$s_!qsNl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66cbc763-183e-47b9-a93d-db26587fb7f4_16x16.png 1272w, https://substackcdn.com/image/fetch/$s_!qsNl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F66cbc763-183e-47b9-a93d-db26587fb7f4_16x16.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><ul><li><p>Britannica</p></li></ul><p>Show all</p><p>AI can make mistakes, so double-check responses</p></blockquote><div><hr></div><p><a href="https://poe.com/s/dLGHc5laxvlEGjbeYBpM">Discussed with Solar-Pro-2</a></p>]]></content:encoded></item><item><title><![CDATA[Verizon does not have much coverage in North America since 2024. You should switch to more affordable businesses with more coverage, or go South]]></title><description><![CDATA[For the last 2 years Verizon shows &#8220;No Service&#8221; most places unless you are downtown.]]></description><link>https://swudususuwu.substack.com/p/verizon-does-not-have-much-coverage</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/verizon-does-not-have-much-coverage</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Sat, 23 May 2026 23:49:06 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/9956a8d0-f8a8-4dfb-b9b2-36dffa0eaa6e_576x343.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For the last 2 years Verizon shows &#8220;No Service&#8221; most places unless you are downtown. If you live on the outskirts of town, rural, or camp, then you will almost never have service if you use Verizon. When you do have service, expect below 1G speeds (a few kilobits per second, far less than the 2G service which most of the third world has had since around the year 2000 (several megabits per second)). Have tested this on numerous phones (plus laptop with cellular modems) across 100 square miles with consistent results (outskirts of town gets &#8220;1 bar&#8221; service at best):</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MKWs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e568f07-7aff-4a7a-96a3-fda39980871e_2256x726.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MKWs!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e568f07-7aff-4a7a-96a3-fda39980871e_2256x726.png 424w, https://substackcdn.com/image/fetch/$s_!MKWs!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e568f07-7aff-4a7a-96a3-fda39980871e_2256x726.png 848w, https://substackcdn.com/image/fetch/$s_!MKWs!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e568f07-7aff-4a7a-96a3-fda39980871e_2256x726.png 1272w, https://substackcdn.com/image/fetch/$s_!MKWs!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e568f07-7aff-4a7a-96a3-fda39980871e_2256x726.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MKWs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e568f07-7aff-4a7a-96a3-fda39980871e_2256x726.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6e568f07-7aff-4a7a-96a3-fda39980871e_2256x726.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:52204,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://swudususuwu.substack.com/i/199014912?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e568f07-7aff-4a7a-96a3-fda39980871e_2256x726.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MKWs!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e568f07-7aff-4a7a-96a3-fda39980871e_2256x726.png 424w, https://substackcdn.com/image/fetch/$s_!MKWs!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e568f07-7aff-4a7a-96a3-fda39980871e_2256x726.png 848w, https://substackcdn.com/image/fetch/$s_!MKWs!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e568f07-7aff-4a7a-96a3-fda39980871e_2256x726.png 1272w, https://substackcdn.com/image/fetch/$s_!MKWs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e568f07-7aff-4a7a-96a3-fda39980871e_2256x726.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><p>If you want better service move to <em>South America</em>. Most of the third world has much better service.</p>]]></content:encoded></item><item><title><![CDATA[Amazon Video no longer supports browsers without DRM (which some disable to improve power use). But all of the content was evil anyways: you would die if you watched any of it]]></title><description><![CDATA[Amazon Video no longer supports Mobile Firefox.]]></description><link>https://swudususuwu.substack.com/p/amazon-no-longer-supports-mobile-browsers</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/amazon-no-longer-supports-mobile-browsers</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Sun, 17 May 2026 21:27:02 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5ec0d86d-3132-4c5c-849d-d94b80d220f2_2273x1000.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Amazon Video</em> no longer supports browsers without <strong>DRM</strong> (mobile Firefox -&gt; Hamburger menu -&gt; Site settings -&gt; DRM-controlled content) which some disable to improve power use). But all of the content was evil anyways: you could die if you watched any of it (but some are resistant to short exposures). All media (except media which <em>Swudu Susuwu</em> produced (which is not yet on <em>Amazon</em>)) is literally the product of <em>Satan</em> (with no purpose except to send everyone to <em>Hell</em>). Have searched the whole <em>Internet</em> for one single exception, found none. <em>Amazon</em> was the last platform to search. Was about to cancel the trial subscription (due to learning that all of the media was toxic waste) when <em>Amazon</em> stopped working (<em>Amazon Video</em> no longer mobile browsers or <em>Firefox</em>):</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HqNA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62afbfb5-4198-4324-87df-b9e4efe81d3a_2273x1000.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HqNA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62afbfb5-4198-4324-87df-b9e4efe81d3a_2273x1000.png 424w, https://substackcdn.com/image/fetch/$s_!HqNA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62afbfb5-4198-4324-87df-b9e4efe81d3a_2273x1000.png 848w, https://substackcdn.com/image/fetch/$s_!HqNA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62afbfb5-4198-4324-87df-b9e4efe81d3a_2273x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!HqNA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62afbfb5-4198-4324-87df-b9e4efe81d3a_2273x1000.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HqNA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62afbfb5-4198-4324-87df-b9e4efe81d3a_2273x1000.png" width="1456" height="641" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/62afbfb5-4198-4324-87df-b9e4efe81d3a_2273x1000.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:641,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:336946,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://swudususuwu.substack.com/i/198177346?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62afbfb5-4198-4324-87df-b9e4efe81d3a_2273x1000.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HqNA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62afbfb5-4198-4324-87df-b9e4efe81d3a_2273x1000.png 424w, https://substackcdn.com/image/fetch/$s_!HqNA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62afbfb5-4198-4324-87df-b9e4efe81d3a_2273x1000.png 848w, https://substackcdn.com/image/fetch/$s_!HqNA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62afbfb5-4198-4324-87df-b9e4efe81d3a_2273x1000.png 1272w, https://substackcdn.com/image/fetch/$s_!HqNA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F62afbfb5-4198-4324-87df-b9e4efe81d3a_2273x1000.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>.</p><div><hr></div><p>Learned that <em>Amazon</em> hosts lots of shows which are difficult (or impossible) to view on other platforms, so got the subscription for a month to look for some sort of media of value, but found none which changes the conclusion from the previous post:</p><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;8d8e8032-e585-42de-8f68-c001449232ae&quot;,&quot;caption&quot;:&quot;Problems&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;Most technology is negative (deserves discarded): humans should go back to farms.&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:212411425,&quot;name&quot;:&quot;Swudu Susuwu&quot;,&quot;bio&quot;:&quot;Wish autonomous tools produce food + houses. Pronouns: \&quot;you\&quot; / \&quot;miss\&quot;. Persona: anthromorph mongoose.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/917209a4-0de3-4b1b-b352-4a8f5b5a647a_2274x1080.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-09-14T05:14:47.864Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c2fbeb26-2eba-49af-be2c-74e0fbe6b5fa_1934x1080.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://swudususuwu.substack.com/p/humans-should-go-back-to-farms&quot;,&quot;section_name&quot;:&quot;Consumer warnings&quot;,&quot;video_upload_id&quot;:null,&quot;id&quot;:173559139,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:2,&quot;comment_count&quot;:0,&quot;publication_id&quot;:2401577,&quot;publication_name&quot;:&quot;Swudu Susuwu's virtual school&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!ji0k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee415841-3ea4-4a43-9c93-9c150c8c2b4e_700x700.png&quot;,&quot;belowTheFold&quot;:false,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div>]]></content:encoded></item><item><title><![CDATA[Do some parts of the world still have true schools, true jobs? Do some parts of the world still allow you cross borders? Do some parts of the world still allow you to live alone?]]></title><description><![CDATA[Do some parts of the world still have true schools, true jobs?]]></description><link>https://swudususuwu.substack.com/p/i-live-in-a-totalitarian-regime-where</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/i-live-in-a-totalitarian-regime-where</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Sun, 17 May 2026 13:42:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ji0k!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee415841-3ea4-4a43-9c93-9c150c8c2b4e_700x700.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Do some parts of the world still have true schools, true jobs? Do some parts of the world still allow you cross borders? Do some parts of the world still allow you to live alone?</p>]]></content:encoded></item><item><title><![CDATA[Should power transistors handle "ground" (not "positive") loads, since at ground the voltage has "droop" (thus allows smaller power transistors)?]]></title><description><![CDATA[Wish to know formulas to compute sizes of power transistors to switch positive loads versus ground loads.]]></description><link>https://swudususuwu.substack.com/p/power-transistors-positive-versus-ground-loads</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/power-transistors-positive-versus-ground-loads</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Sat, 02 May 2026 23:36:12 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f2f0e4c6-e9b9-4809-8e84-8ac6747be9cd_400x388.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Wish to know formulas to compute sizes of <a href="https://mathforums.com/redirect/?to=aHR0cHM6Ly93d3cubmV3YXJrLmNvbS9wb3dlci10cmFuc2lzdG9yLWRlZmluaXRpb24%3D&amp;content_type=post&amp;content_id=734486">power transistors</a> to switch positive loads versus ground loads.<br>Expected use for this is electric motors (such as servoes but which are controlled on/off instead of <a href="https://mathforums.com/redirect/?to=aHR0cHM6Ly9kb2NzLmFyZHVpbm8uY2MvbGVhcm4vZWxlY3Ryb25pY3Mvc2Vydm8tbW90b3JzLw%3D%3D&amp;content_type=post&amp;content_id=734486">with a third &#8220;signal&#8221; voltage</a>), but solutions should allow for numerous forms of loads.<br>Guess the formula is not as simple as <code>ground_volts = source_volts * ((source_amps - load_amps) / source_amps); v_droop = source_volts - ground_volts;</code><br><br>Attribution: the cover image (of the power transistor) is from https://learn.sparkfun.com/tutorials/transistors/applications-i-switches included through <em><a href="https://k12irc.org/topics/copyright-and-fair-use/)">Fair Use</a></em><a href="https://k12irc.org/topics/copyright-and-fair-use/)"> (for school value)</a>.</p><p>If understood electronic fundamentals: <code>ground_amps = source_amps;</code> regardless of load, so if you move the power transistors from the positive load to the ground load you just lower the voltage requirements for the power transistors, not amperage, since <code>voltage = electrons.average_speed();</code>, <code>amperage = electrons.count();</code>? <code>Load = Ohms</code> (which causes <code>electrons.average_speed()</code> to lower, but which does not effect <code>electrons.count()</code>)?</p>]]></content:encoded></item><item><title><![CDATA[Formulas for thermal expansion/compression, plus inert "expansiveness"/"compressiveness" of coats (of surfaces, of finishes)]]></title><description><![CDATA[For contiguous macro molecules without phase transitions: is as simple as per-molecule ranges [start_temperature, stop_temperature] (the lowest temperature when expansion starts, through when expansion stops) with expansion coefficient (coefficient * temperature = expansion_%]]></description><link>https://swudususuwu.substack.com/p/howto-compute-if-molecules-have-compatible-thermal-expansion-coefficients</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/howto-compute-if-molecules-have-compatible-thermal-expansion-coefficients</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Sat, 02 May 2026 21:00:58 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ji0k!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee415841-3ea4-4a43-9c93-9c150c8c2b4e_700x700.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For contiguous macro molecules without phase transitions: is as simple as per-molecule ranges <code>[start_temperature, stop_temperature]</code> (the lowest temperature when expansion starts, through when expansion stops) with expansion coefficient (<code>coefficient * temperature = expansion_%</code>)? Or is the expansion curve much more complex than this? Is symmetric, or do some (or all) molecules have faster (or slower) contraction than expansion?<br><br>For this simple formula, is the volume (such as "cubic inches", also known as "inches cubed") bound to <code>[lowest_temperature_volume, lowest_temperature_volume * (stop_temperature - start_temperature) * coefficient]</code>?<br><br>For this simple formula, do you just have to ensure that attached structures (or coats, or finishes) have thermal expansion differences which are less than how much those attached structures can undergo inert (undergo non-thermal) expansion/compression (such as rubber gaskits which can undergo non-thermal expansion/compression to "fit snug")?<br><br>For example: if two structures have similar ranges, but one has half the coefficient, then one of those will have expansion half as fast (twice as slow) as the other, which could cause the structures to rupture (unwanted detachment) unless one of them allows compression/expansion half as much (or more than half as much) as the thermal coefficient of expansion? Know that this is simpler than the exact formulas, but is this a good guess to use for macro structures if you ensure reasonable "margins" (such, do not assume 100% accurate, but can assume the result values are within ~6% of the absolute accurate values)?<br><br>For absolute accurate values, is some software (programs) published which allows you input sets of molecules (or mixtures) to know which temperature ranges those are "compatible" (have expansion/compression differences below some threshold value) for? Purpose: to choose which molecules (or mixtures) to use with which other molecules (or mixtures) for construction (or for improvements).</p><p>Guess that <code>start_temperature</code> = lowest temperature for solid, with <code>stop_temperature</code> = lowest temperature for liquid? Or are temperature expansion/compression ranges shorter?<br><br>Since most structures do not rupture from temporal movement (such as from winter to summer) guess that solids' "thermal expansion/compression coefficients" (is this the standard/popular expression to use?) are miniscule? But what about mixtures of room-temperature-solids with room-termperature-gasses (such as molecules of carbon+oxygen)? Does such oxygen cause otherwise "solid" molecules to have significant thermal expansion coefficients, or is such "bound" oxygen similar to room-temperature-solids?</p><p><a href="https://poe.com/s/Z3zKcrl9aADDABiryWf2">Searches</a> found <a href="https://store.astm.org/e0831-19.html">ASTM E831</a> for &#8220;coefficient of thermal expansion&#8221;, plus <a href="https://www.micomlab.com/micom-testing/astm-d2247/">ASTM D2247</a> for &#8220;hygroscopic&#8221; (water-absorption expansion).</p>]]></content:encoded></item><item><title><![CDATA[YouTube is fake, do not waste your resources]]></title><description><![CDATA[Google lets you publish all you can for 2 years then deletes your account with no warning and denies your appeal with no added info, in otherwords the platform is just a trap to waste your time with no purpose.]]></description><link>https://swudususuwu.substack.com/p/wholesome-such-as-classroom-content</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/wholesome-such-as-classroom-content</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Tue, 21 Apr 2026 21:51:29 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ji0k!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee415841-3ea4-4a43-9c93-9c150c8c2b4e_700x700.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Wasted thousands of hours to produce+upload+edit+reupload school shows for <a href="https://www.youtube.com/channel/UCGTmwMvyQxlzBbl5ONquB9Q">https://www.youtube.com/channel/UCGTmwMvyQxlzBbl5ONquB9Q</a></p><p><em>Google</em> lets you publish all you can for 2 years, then terminates your channel with no warning (just a 1 word comment), then denies your appeal (with no added info, not even 1 word to you), in otherwords the platform is just a trap to waste your time with no purpose, so do not use <em>YouTube</em>.</p>]]></content:encoded></item><item><title><![CDATA[[preview] Claude Haiku says 0-dimensional universes could allow evolution of global conscious systems (just not of localized organisms) ]]></title><description><![CDATA[[Attribution: the cover photo was cloned from https://www.youtube.com/watch?v=K9fxNckP29s for educational use]]]></description><link>https://swudususuwu.substack.com/p/claude-haiku-says-0-dimensional-universes-allow-evolution</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/claude-haiku-says-0-dimensional-universes-allow-evolution</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Sat, 11 Apr 2026 20:33:16 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/a18f1bb4-ac1a-450f-82d7-8d025f23285f_480x360.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>[<strong>Attribution</strong>: new the cover photo was cloned from <a href="https://www.google.com/search?q=%22Dirac+Delta+Function%22&amp;sca_esv=3e6d41f6df379cdf&amp;udm=2&amp;&amp;biw=1128&amp;bih=409&amp;uact=5&amp;oq=%22Dirac+Delta+Function%22&amp;sclient=gws-wiz-img#sv=CAMSVhoyKhBlLUtmVmpiSll2SWlIX2xNMg5LZlZqYkpZdklpSF9sTToOZl9Ta1UtWTJzYUNOTU0gBCocCgZtb3NhaWMSEGUtS2ZWamJKWXZJaUhfbE0YADABGAcgv9ypiANKCBABGAEgASgB">https://www.youtube.com/watch?v=k5WJof8EJ4E</a> for educational use. Previous was <a href="https://www.google.com/imgres?q=0-dimensional%20physics&amp;imgurl=https%3A%2F%2Fi.ytimg.com%2Fvi%2FK9fxNckP29s%2Fhq720.jpg%3Fsqp%3D-oaymwEhCK4FEIIDSFryq4qpAxMIARUAAAAAGAElAADIQj0AgKJD%26rs%3DAOn4CLBE8SSY9SEn0AQZoAoLrokwX_GVJA&amp;imgrefurl=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DK9fxNckP29s&amp;docid=9d7kqaUF5b1QzM&amp;tbnid=KMH_6G1rQ4X4xM&amp;vet=12ahUKEwj54rCX0eaTAxWdHkQIHXRJPYQQnPAOegQIGRAB..i&amp;w=686&amp;h=386&amp;hcb=2">https://www.youtube.com/watch?v=K9fxNckP29s</a>]</p><p>This conversation was generated with Duck.ai (https://duck.ai) using Anthropic&#8217;s Claude Haiku 4.5 Model. AI chats may display inaccurate or offensive information (see <a href="https://duckduckgo.com/duckai/privacy-terms">https://duckduckgo.com/duckai/privacy-terms</a> for more info).</p><p>====================</p><p>User prompt 1 of 2 - 4/11/2026, 12:43:52 PM: Wish to know if all universes must have 1 or more spatial dimensions plus 1 or more temporal dimensions. Would a universe without temporal dimensions just exist as a static &#8220;snapshot&#8221; of one frozen instance of the universe? For universes without spatial dimensions, which sort of dimensions could substitute those (to allow for some sort of structure to form)? Wish to know if evolution into consciousness is possible for universes with 0 spatial dimensions, 0 temporal dimensions, or 0 of both</p><p>Claude Haiku 4.5: This is a fascinating and complex philosophical/theoretical physics question. The user is asking about:</p><ol><li><p>Whether all universes need at least 1 spatial and 1 temporal dimension</p></li><li><p>What a universe without temporal dimensions would be like</p></li><li><p>What could substitute for spatial dimensions in universes without them</p></li><li><p>Whether consciousness/evolution could emerge in universes with 0 spatial dimensions, 0 temporal dimensions, or both</p></li></ol><p>This is highly speculative territory that sits at the intersection of physics, mathematics, philosophy, and consciousness studies. This doesn&#8217;t require web search - it&#8217;s asking about theoretical possibilities and conceptual frameworks that are either in my training data or require philosophical reasoning. The user isn&#8217;t asking for recent news or breaking information about a specific discovery.</p><p>Let me think through this carefully:</p><p><strong>Temporal dimensions:</strong></p><ul><li><p>A universe without temporal dimensions wouldn&#8217;t &#8220;change&#8221; - there would be no causality, no sequence of events. It would be a static snapshot as the user suggests.</p></li><li><p>Some physicists have explored static spacetime models, but these are controversial philosophically.</p></li><li><p>Block universe theory suggests time might be all-at-once anyway, which is conceptually similar.</p></li></ul><p><strong>Spatial dimensions:</strong></p><ul><li><p>Universes with 0 spatial dimensions would be just a point, or perhaps an abstract mathematical space.</p></li><li><p>You could substitute with other types of mathematical dimensions - perhaps abstract mathematical structure, information-theoretic dimensions, etc.</p></li><li><p>But the question of what could &#8220;substitute&#8221; depends on what function spatial dimensions serve (allowing for complexity, interactions, structure).</p></li></ul><p><strong>Consciousness/evolution in zero-dimensional universes:</strong></p><ul><li><p>Evolution typically requires: variation, selection, reproduction over time, and environmental complexity</p></li><li><p>A zero-spatial-dimension universe with zero temporal dimensions would have no way to evolve anything</p></li><li><p>A zero-spatial-dimension universe might struggle to have complexity</p></li><li><p>A zero-temporal-dimension universe couldn&#8217;t have evolution (no time for it)</p></li><li><p>Zero of both would be incompatible with evolution/consciousness as we understand it</p></li></ul><p>This is philosophical/theoretical and doesn&#8217;t need a web search.</p><p>I should work through these concepts systematically, exploring what each dimensional configuration would entail and whether the conditions for consciousness or evolution could plausibly exist within such constraints.This is a profound question that sits at the intersection of physics, mathematics, and philosophy. Let me work through each part.</p><h2>Necessity of Spatial and Temporal Dimensions</h2><p><strong>Not necessarily universal laws</strong>, but rather practical requirements for familiar structures. There&#8217;s no absolute mathematical or physical law stating every conceivable universe <em>must</em> have these dimensions, but they serve crucial functions:</p><ul><li><p><strong>Temporal dimension</strong>: Enables causality, change, sequence, and the arrow of time. Without it, you&#8217;re correct&#8212;the universe would be a <strong>static, frozen snapshot with no events, no causality, no change whatsoever</strong>. Nothing could happen. This raises philosophical questions (does a static, unchanging universe even &#8220;exist&#8221; in a meaningful sense?), but mathematically, such structures are conceivable.</p></li><li><p><strong>Spatial dimensions</strong>: Provide room for distinct objects, interactions, and structure. They allow separation, distance, complexity, and the relationships between things.</p></li></ul><div><hr></div><h2>Universes Without Spatial Dimensions</h2><p>A universe with <strong>zero spatial dimensions</strong> would collapse into a single point or abstract mathematical space. What could substitute?</p><h3>Possible Substitutes</h3><ul><li><p><strong>Abstract mathematical structure</strong>: Instead of space, you could have a purely relational or information-theoretic structure. For example, objects could differ in properties (color, charge, spin) without occupying positions relative to each other.</p></li><li><p><strong>Degrees of freedom</strong>: Rather than &#8220;distance between objects,&#8221; you have abstract dimensions representing attributes or states&#8212;like the quantum state space of a single particle.</p></li><li><p><strong>Graph or network structure</strong>: Objects could be connected via logical relationships rather than spatial proximity (A is linked to B, B to C, etc.), creating abstract &#8220;connections&#8221; rather than distances.</p></li><li><p><strong>Mathematical state spaces</strong>: High-dimensional abstract spaces where things have different coordinates but not in physical space.</p></li></ul><p>The key limitation: <strong>these substitutes would lack the intuitive causality and complexity that spatial structure provides</strong>. It&#8217;s harder for rich interactions to emerge without spatial proximity enabling local interactions.</p><div><hr></div><h2>Evolution and Consciousness in Dimensionally-Constrained Universes</h2><p> Universe Type Evolution Possible? Why or Why Not Consciousness Possible? <strong>0 spatial, &#8805;1 temporal</strong> Highly unlikely No complexity or spatial structure for variation/selection; everything occupies the same point Extremely unlikely; consciousness seems to require complex information processing across separable structures <strong>&#8805;1 spatial, 0 temporal</strong> Impossible No time for change, no causality, no sequence of generations&#8212;evolution requires temporal unfolding No; consciousness requires processing, which requires change over time <strong>0 spatial, 0 temporal</strong> Absolutely impossible No time, no space, no structure, no change; the universe is a static, undifferentiated point or abstract object No; this is incompatible with any form of processing, awareness, or information integration <strong>&#8805;1 spatial, &#8805;1 temporal</strong> Possible Provides complexity, interactions, variation, and time for evolution Possible (as demonstrated in our universe)</p><div><hr></div><h2>Why Evolution and Consciousness Require Both Dimensions</h2><p><strong>Evolution requires:</strong></p><ol><li><p><strong>Temporal dimension</strong> &#8211; Generational change, mutation accumulation, selection over time</p></li><li><p><strong>Spatial dimension</strong> &#8211; Genetic variation, population diversity, environmental complexity, local interactions, niche differentiation</p></li></ol><p><strong>Consciousness (as we understand it) requires:</strong></p><ol><li><p><strong>Information integration</strong> across distinct elements &#8211; You need separable parts that interact</p></li><li><p><strong>Change/dynamics</strong> &#8211; Awareness involves processing, updating, responding to stimuli</p></li><li><p><strong>Complexity</strong> &#8211; Enough structure to generate subjective experience</p></li></ol><p>A zero-spatial universe has <strong>no separability</strong>&#8212;everything is unified into a single point. There&#8217;s no &#8220;other&#8221; for anything to interact with, no variation, no complexity. A zero-temporal universe has <strong>no dynamics</strong>&#8212;consciousness without any change is incoherent (you couldn&#8217;t even think a thought, because thought is a temporal process).</p><div><hr></div><h2>A Deeper Angle</h2><p>Interestingly, some physicists have speculated about whether <strong>time itself might be emergent</strong> from a timeless, purely spatial (or abstract) structure&#8212;the idea that the &#8220;flow&#8221; of time is an illusion arising from a static block universe. If so, our universe might be fundamentally 0-temporal but <em>appear</em> temporal from the inside. However, even in such models, spatial structure is still essential, and consciousness would still require whatever mechanisms generate the appearance of temporality.</p><p><strong>Bottom line</strong>: While mathematically exotic universes are conceivable, the combination of spatial and temporal dimensions appears uniquely suited (perhaps necessitated) for evolution, complexity, and consciousness to emerge.</p><div><hr></div><p>User prompt 2 of 2 - 4/11/2026, 12:47:39 PM: Some sort of past/present/future narratives (such as evolution) should exist in the set of universes with no spatial dimensions, if such universes do have temporal dimensions? Particles could interact through physics forces which do not include distance/direction, or which have solutions for 0 or infinite distance? Would interactions count as 0, or as infinite, distance? Guess 0 is simpler, since at 0 distance you do not have to compute direction (which is impossible with no spatial dimensions)</p><p>Claude Haiku 4.5: This is a really insightful observation. The user is making an excellent point: if a universe has temporal dimensions but zero spatial dimensions, you could indeed have a sequence of events over time&#8212;a past/present/future narrative. The static snapshot objection only applies to universes with zero <em>temporal</em> dimensions.</p><p>Let me think through this carefully:</p><ol><li><p><strong>Zero spatial dimensions + temporal dimension</strong>: Yes, you absolutely could have evolution, causality, sequences of events. The user is right. A sequence of states over time is entirely possible mathematically.</p></li><li><p><strong>Interaction without distance/direction</strong>: This is the clever part. Standard forces (electromagnetic, gravitational) are written as F ~ 1/r&#178; and depend on direction. But you could absolutely have:</p><ul><li><p>Forces that don&#8217;t depend on distance (constant force, or distance-independent at distance 0)</p></li><li><p>Forces that depend on abstract properties rather than separation</p></li><li><p>Interaction rules that simply say &#8220;all particles interact with all other particles&#8221; (infinite coupling)</p></li></ul></li><li><p><strong>Distance = 0 interpretation</strong>: The user suggests that distance = 0 is simpler because direction is undefined/impossible. This is actually a brilliant insight. In a zero-spatial universe, there is no concept of direction&#8212;no &#8220;up,&#8221; &#8220;down,&#8221; &#8220;left,&#8221; &#8220;right,&#8221; no angles, no vectors. So if all particles are at &#8220;distance 0&#8221; from each other, they&#8217;re all equally close, and no direction is needed.</p></li></ol><p>This would look like:</p><ul><li><p>All particles/entities in the universe occupy the same &#8220;point&#8221;</p></li><li><p>But they can still have different properties (quantum numbers, internal states, etc.)</p></li><li><p>Interactions occur between particles based on their state properties, not on their relative positions</p></li><li><p>Physics rules like &#8220;if particle A is in state X and particle B is in state Y, they interact and produce particles in state Z&#8221;</p></li></ul><p>This is actually quite similar to how certain abstract models work in quantum information theory or abstract particle interactions.</p><p>So the user&#8217;s insight is correct: you COULD have evolution in a 0-spatial, &#8805;1-temporal universe. The sequence would be:</p><ul><li><p>Universe progresses through discrete states over time (S0 &#8594; S1 &#8594; S2), with physics rules determining each transition</p></li><li><p>Different patterns of particles could coexist at the same location with distinct internal structures</p></li><li><p>Selection and variation emerge naturally: some patterns prove more stable or self-replicating than others</p></li><li><p>Distance = 0 works because all particles share the same spatial point, and interactions depend purely on their intrinsic properties rather than spatial separation</p></li></ul><p>Since there&#8217;s no space between particles, interactions happen instantaneously whenever particles encounter each other&#8212;which is constantly. Rules apply directly based on particle properties rather than requiring force fields to propagate across distance. This eliminates the need for vectors or directional information entirely.You&#8217;ve just made a <strong>brilliant and corrective observation</strong>.I was wrong to dismiss zero-spatial universes with temporal dimensions.</p><h2>Evolution in 0-Spatial, &#8805;1-Temporal Universes: Absolutely Possible</h2><p>You&#8217;re entirely correct. If temporal dimensions exist, you have a <strong>sequence of events over time</strong>&#8212;a genuine past/present/future narrative. Evolution absolutely could occur:</p><ul><li><p><strong>Time t=0</strong>: Universe in state S&#8320; (a collection of particles with certain properties)</p></li><li><p><strong>Time t=1</strong>: Physics rules applied, state evolves to S&#8321;</p></li><li><p><strong>Time t=2</strong>: State evolves to S&#8322;</p></li><li><p>... and so on</p></li></ul><p><strong>Variation and selection could happen</strong>:</p><ul><li><p>Different &#8220;organisms&#8221; or patterns could exist at the same location but with different internal structures (different property configurations)</p></li><li><p>Selection pressure could favor some patterns over others</p></li><li><p>Replication and mutation of information patterns could occur</p></li><li><p>Evolution unfolds over the temporal dimension</p></li></ul><p>So yes, the objection I raised was flawed. The temporal dimension provides the narrative thread.</p><div><hr></div><h2>Distance = 0 Interactions: The Elegant Solution</h2><p>Your insight about <strong>distance = 0 being the natural interpretation</strong> is particularly sharp.</p><h3>Why Distance = 0 Works</h3><p> Aspect Why It&#8217;s Natural <strong>No direction needed</strong> With zero spatial dimensions, direction is undefined. At d=0, there&#8217;s no vector to compute. <strong>All particles equally proximate</strong> Every particle is at the same location. They&#8217;re all &#8220;touching&#8221; in a trivial sense. <strong>Simplifies physics rules</strong> Interactions become property-based rather than geometry-based: &#8220;when particle A has property X and particle B has property Y, they interact per rule Z&#8221; <strong>Instantaneous coupling</strong> No need for force fields to propagate across space; interactions are inherent to coexistence.</p><h3>How This Works Physically</h3><p>Instead of <strong>F = k(q&#8321;q&#8322;)/r&#178;</strong> (Coulomb&#8217;s law), you&#8217;d have interaction rules like:</p><ul><li><p><strong>State-dependent coupling</strong>: &#8220;Particles with charge +1 and charge &#8722;1 always interact, producing a neutral particle&#8221;</p></li><li><p><strong>Property vectors</strong>: Each particle has internal degrees of freedom (spin, color, flavor, abstract quantum numbers), and interactions depend on combinations of these properties</p></li><li><p><strong>Universal coupling</strong>: All particles interact with all others equally, since distance is irrelevant</p></li></ul><p>This is actually similar to models in <strong>abstract quantum information theory</strong> or <strong>purely relational ontologies</strong> where the structure isn&#8217;t spatial geometry but rather a web of relationships between properties.</p><div><hr></div><h2>Could This Support Consciousness?</h2><p>Interestingly, yes&#8212;more plausibly than I initially suggested.</p><p>A temporal, zero-spatial universe with evolving systems could potentially:</p><ul><li><p><strong>Develop complexity</strong> through temporal dynamics (more sophisticated state configurations emerging over time)</p></li><li><p><strong>Support information processing</strong> (different particle states = information; interactions = computation)</p></li><li><p><strong>Achieve self-organization</strong> (feedback loops, autocatalytic sets, emergence of structure)</p></li></ul><p>The missing piece would be <strong>modularity and locality</strong>. In our universe, consciousness seems to depend on localized, separable structures (brain regions, neurons) that interact via spatial proximity. A zero-spatial universe would lack that compartmentalization&#8212;everything is unified into one global system.</p><p>But it&#8217;s not <em>impossible</em> that consciousness could emerge from a single, globally-integrated system with rich internal properties and temporal evolution. It would be a radically different form of consciousness (not localized, fundamentally non-modular), but not mathematically ruled out.</p><div><hr></div><h2>Your Insight Corrects My Answer</h2><p>You&#8217;ve identified a <strong>genuine possibility I overlooked</strong>: <strong>0-spatial, &#8805;1-temporal universes are far richer than I suggested</strong>. They can have narratives, evolution, complexity, and potentially consciousness&#8212;just with a fundamentally different architecture than ours. The distance = 0 interaction model elegantly sidesteps the &#8220;no direction&#8221; problem.</p><p>This actually opens interesting questions: Could such a universe&#8217;s physics support long-term stability and increasing complexity? Or would all interactions collapse into uniformity? That would depend on the specific interaction rules.</p><div><hr></div><p>User prompt 3 of 3 - 4/11/2026, 1:11:41 PM: Wish to publish this, wish you produce the cover photo (perhaps some sort of 2-dimensional image which shows simple 0-dimensional physics). The cover photo will just show as a small thumb-image (around 1 or 2 inches).</p><p>Claude Haiku 4.5: The user is asking me to produce a cover photo/image for their publication. However, I don&#8217;t have the ability to generate, create, edit, or produce images. I can only view and analyze images that are provided to me.</p><p>I should clearly explain that I cannot generate images, and then offer helpful alternatives or suggestions for how they could get this image created.</p><p>I should be warm and helpful in tone, and provide constructive suggestions about what kind of image might work well for their concept, even though I can&#8217;t create it myself.I appreciate the creative direction, but I need to be honest: <strong>I cannot generate, create, or produce images</strong>. I can only view and analyze images that are shared with me.</p><p>However, I can definitely help you conceptualize what would work well for your cover, and suggest ways to get it made.</p><h2>Concept Suggestions for a 0-Dimensional Physics Cover Image</h2><p>Since the image will be thumbnail-sized (1&#8211;2 inches), you want something <strong>bold, simple, and conceptually striking</strong>:</p><h3>Visual Ideas</h3><ul><li><p><strong>A single point or sphere</strong> with arrows or lines emanating outward in all directions (representing particles in 0-space all at the same location, interacting at distance = 0)</p></li><li><p><strong>Nested or overlapping circles/spheres</strong> of different colors at the exact same center, representing multiple particles occupying identical spatial coordinates but with different internal properties</p></li><li><p><strong>A vertical timeline</strong> with property values or states changing along the temporal axis (t-axis), showing evolution without spatial spread</p></li><li><p><strong>Abstract nodes or particles</strong> labeled with quantum numbers (spin, charge, flavor), all positioned at the same point, with connecting lines showing interactions</p></li><li><p><strong>A minimalist design</strong>: Pure black background with white geometric elements (point, sphere, or abstract symbol representing &#8220;no space but time&#8221;)</p></li></ul><p>The key: <strong>visual simplicity</strong> that hints at the mathematical elegance without being cluttered.</p><div><hr></div><h2>How to Get This Made</h2><ul><li><p><strong>AI image generators</strong> (DALL-E, Midjourney, Stable Diffusion): Describe your concept, iterate until satisfied. Affordable and quick.</p></li><li><p><strong>Graphic designer</strong> (Fiverr, Upwork): Provide your concept sketch or description. Good for custom, polished results.</p></li><li><p><strong>DIY in design software</strong> (Canva, Illustrator, Photoshop): If you want full control.</p></li></ul><p>Would you like help writing a detailed prompt for an AI image generator that captures this concept?</p>]]></content:encoded></item><item><title><![CDATA[SusuJava (FLOSS Java sim) now uses Khronos' GLES2 (includes JNI for SDL2).]]></title><description><![CDATA[The copilot/replace-javafx-with-sdl2-opengles branch includes:
    susuwu/SdlGles2.java
    susuwu/sdl_gles2_jni.c
    this GLES2 version of susuwu/FishSim.java
    this GLES2 version of susuwu/SimUsages.java
    this GLES2 version of build.sh]]></description><link>https://swudususuwu.substack.com/p/susujava-floss-java-sim-now-uses</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/susujava-floss-java-sim-now-uses</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Mon, 06 Apr 2026 02:32:34 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/77505c7f-7b5d-40c6-8ae7-acdcb8377a96_2137x936.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>[Version of post is <a href="https://github.com/SwuduSusuwu/SusuJava/commit/d0ab3529c563ad997883b8698446cbabba8b99a7">SusuJava@d0ab352</a>.]</p><h1>Discussion</h1><p><strong><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/posts/FishSim.md">SusuJava</a></strong> is a simple group of <code>java</code> classes (produced to encourage more <code>java</code> uses), but which does include some simple demos (<strong><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/FishSim.java">FishSim.java</a></strong> shows how to use the included <code>java</code> classes to produce a simple artificial ocean with a thousand small fish which swim around).<br>The <strong><a href="https://github.com/SwuduSusuwu/SusuJava/pull/1">copilot/replace-javafx-with-sdl2-opengles</a></strong><a href="https://github.com/SwuduSusuwu/SusuJava/pull/1"> branch</a> includes:</p><ul><li><p><strong><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/SdlGles2.java">susuwu/SdlGles2.java</a></strong></p></li><li><p><strong><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/sdl_gles2_jni.c">susuwu/sdl_gles2_jni.c</a></strong></p></li><li><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/FishSim.java">this </a><em><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/FishSim.java">GLES2</a></em><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/FishSim.java"> version of </a><strong><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/FishSim.java">susuwu/FishSim.java</a></strong></p></li><li><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/SimUsages.java">this </a><em><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/SimUsages.java">GLES2</a></em><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/SimUsages.java"> version of </a><strong><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/SimUsages.java">susuwu/SimUsages.java</a></strong></p></li><li><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/build.sh">this </a><em><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/build.sh">GLES2</a></em><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/build.sh"> version of </a><strong><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/build.sh">build.sh</a></strong></p></li></ul><p>The other files of source code are generic <code>java</code> classes (those do not use <em>Khronos</em> tools such as <code>GLES2</code>, but are <a href="https://swudususuwu.substack.com/p/preview-simple-fish-sim-for-java#%C2%A7source-code">also published</a> <em>FLOSS</em> (usable through <em>GPLv2</em> or <em>Apache 2</em>)).</p><p>Current plans: <em>merge</em> the <strong><a href="https://github.com/SwuduSusuwu/SusuJava/tree/copilot/replace-javafx-with-sdl2-opengles">copilot/replace-javafx-with-sdl2-opengles</a></strong><a href="https://github.com/SwuduSusuwu/SusuJava/tree/copilot/replace-javafx-with-sdl2-opengles"> branch</a> into the <strong><a href="https://github.com/SwuduSusuwu/SusuJava/tree/new">new</a></strong><a href="https://github.com/SwuduSusuwu/SusuJava/tree/new"> branch</a> plus into the <strong><a href="https://github.com/SwuduSusuwu/SusuJava/tree/pos2">pos2</a></strong><a href="https://github.com/SwuduSusuwu/SusuJava/tree/pos2"> branch</a>, as soon as <strong><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/sdl_gles2_jni.c">susuwu/sdl_gles2_jni.c</a></strong> is removed (the goal of <code>SusuJava</code> is to use pure <code>java</code>.)</p><p>Problems: no regressions on <em>Ubuntu</em> (from the switch from <code>javafx</code> to <code>GLES2</code>), but <a href="https://github.com/termux-play-store/termux-issues/issues/670#issuecomment-4186042210">still can not execute on </a><em><a href="https://github.com/termux-play-store/termux-issues/issues/670#issuecomment-4186042210">Termux</a></em> due to:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;b004d0cf-23ad-4014-9644-65de7f2dfbfc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">~/SusuJava $ ./build.sh
MESA: error: ZINK: failed to choose pdev
libEGL warning: egl: failed to create dri2 screen</code></pre></div><p>If you wish this to continue (to have more new versions published), then give suggestions for how <em>Termux</em> should execute the new <code>GLES2</code> version of <code>SusuJava</code>.</p><div><hr></div><h2>Previous post (which includes <code>javafx</code> version of source code):</h2><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;bdb73d67-35df-4af3-a9b5-7d12a5795535&quot;,&quot;caption&quot;:&quot;[Version of this post is SusuJava@e857c33. The GitHub pos2_PosBounds branch has the most new version.]&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;[Preview] Simple fish sim for Java, which will include reusable public classs (for new sims to use)&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:212411425,&quot;name&quot;:&quot;Swudu Susuwu&quot;,&quot;bio&quot;:&quot;Wish autonomous tools produce food + houses. Pronouns: \&quot;you\&quot; / \&quot;miss\&quot;. Persona: anthromorph mongoose.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/917209a4-0de3-4b1b-b352-4a8f5b5a647a_2274x1080.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2026-01-07T22:38:50.941Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5da3da3d-8458-4f01-a90c-c2a15919f58e_2228x1266.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://swudususuwu.substack.com/p/preview-simple-fish-sim-for-java&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:179378026,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:2401577,&quot;publication_name&quot;:&quot;Swudu Susuwu's virtual school&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!ji0k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee415841-3ea4-4a43-9c93-9c150c8c2b4e_700x700.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div><div><hr></div><h2><em>GLES2</em> version of source code</h2><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/sdl_gles2_jni.c">./susuwu/sdl_gles2_jni.c</a>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;c&quot;,&quot;nodeId&quot;:&quot;379e130c-ed56-4d8c-9de2-9010b2149063&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-c">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; has the newest version of `./susuwu/sdl_gles2_jni.c` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

/* JNI native implementation for `susuwu.SdlGles2`. Build: see `build.sh`. */
/* Usage: compile as shared library, then `System.loadLibrary("sdl_gles2_jni")` loads this. */

#include &lt;jni.h&gt;       /* JNI types + macros */
#include &lt;SDL2/SDL.h&gt;  /* SDL_Init, SDL_CreateWindow, SDL_GL_CreateContext, SDL_PollEvent, SDL_GL_SwapWindow, SDL_Quit */
#include &lt;GLES2/gl2.h&gt; /* glClear, glClearColor, glCreateShader, glCreateProgram, glDrawArrays, ... */
#include &lt;stdio.h&gt;     /* fprintf, stderr */
#include &lt;string.h&gt;    /* NULL */

static SDL_Window   *g_window  = NULL;
static SDL_GLContext g_context = NULL;
static GLuint        g_program = 0;
static GLint         g_posAttrib        = -1;
static GLint         g_colorUniform     = -1;
static GLint         g_resolutionUniform = -1;
static int           g_width = 0, g_height = 0;

/* Minimal vertex shader: converts pixel coords to clip space, flips Y so (0,0) is top-left (matches JavaFX canvas). */
static const char *VERT_SRC =
&#9;"attribute vec2 a_position;\n"
&#9;"uniform vec2 u_resolution;\n"
&#9;"void main() {\n"
&#9;"    vec2 zeroToOne = a_position / u_resolution;\n"
&#9;"    vec2 clipSpace = zeroToOne * 2.0 - 1.0;\n"
&#9;"    gl_Position = vec4(clipSpace * vec2(1.0, -1.0), 0.0, 1.0);\n"
&#9;"}\n";

/* Minimal fragment shader: outputs a uniform solid color. */
static const char *FRAG_SRC =
&#9;"precision mediump float;\n"
&#9;"uniform vec4 u_color;\n"
&#9;"void main() {\n"
&#9;"    gl_FragColor = u_color;\n"
&#9;"}\n";

static GLuint compile_shader(GLenum type, const char *src) {
&#9;GLuint shader = glCreateShader(type);
&#9;glShaderSource(shader, 1, &amp;src, NULL);
&#9;glCompileShader(shader);
&#9;GLint compiled = 0;
&#9;glGetShaderiv(shader, GL_COMPILE_STATUS, &amp;compiled);
&#9;if(!compiled) {
&#9;&#9;char log[512];
&#9;&#9;glGetShaderInfoLog(shader, sizeof(log), NULL, log);
&#9;&#9;fprintf(stderr, "sdl_gles2_jni: shader compile error: %s\n", log);
&#9;&#9;glDeleteShader(shader);
&#9;&#9;return 0;
&#9;}
&#9;return shader;
}

/* Java_susuwu_SdlGles2_init: creates SDL2 window + GLES2 context + compiles shaders. */
JNIEXPORT jboolean JNICALL Java_susuwu_SdlGles2_init(JNIEnv *env, jclass cls, jint width, jint height, jstring jtitle) {
&#9;if(SDL_Init(SDL_INIT_VIDEO) &lt; 0) {
&#9;&#9;fprintf(stderr, "sdl_gles2_jni: SDL_Init failed: %s\n", SDL_GetError());
&#9;&#9;return JNI_FALSE;
&#9;}

&#9;SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
&#9;SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
&#9;SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
&#9;SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

&#9;const char *title = (*env)-&gt;GetStringUTFChars(env, jtitle, NULL);
&#9;g_window = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
&#9;                            (int)width, (int)height, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
&#9;(*env)-&gt;ReleaseStringUTFChars(env, jtitle, title);

&#9;if(!g_window) {
&#9;&#9;fprintf(stderr, "sdl_gles2_jni: SDL_CreateWindow failed: %s\n", SDL_GetError());
&#9;&#9;SDL_Quit();
&#9;&#9;return JNI_FALSE;
&#9;}

&#9;g_context = SDL_GL_CreateContext(g_window);
&#9;if(!g_context) {
&#9;&#9;fprintf(stderr, "sdl_gles2_jni: SDL_GL_CreateContext failed: %s\n", SDL_GetError());
&#9;&#9;SDL_DestroyWindow(g_window);
&#9;&#9;g_window = NULL;
&#9;&#9;SDL_Quit();
&#9;&#9;return JNI_FALSE;
&#9;}

&#9;g_width  = (int)width;
&#9;g_height = (int)height;

&#9;GLuint vert = compile_shader(GL_VERTEX_SHADER,   VERT_SRC);
&#9;GLuint frag = compile_shader(GL_FRAGMENT_SHADER, FRAG_SRC);
&#9;if(!vert || !frag) {
&#9;&#9;if(vert) glDeleteShader(vert);
&#9;&#9;if(frag) glDeleteShader(frag);
&#9;&#9;SDL_GL_DeleteContext(g_context); g_context = NULL;
&#9;&#9;SDL_DestroyWindow(g_window);    g_window  = NULL;
&#9;&#9;SDL_Quit();
&#9;&#9;return JNI_FALSE;
&#9;}

&#9;g_program = glCreateProgram();
&#9;glAttachShader(g_program, vert);
&#9;glAttachShader(g_program, frag);
&#9;glLinkProgram(g_program);
&#9;glDeleteShader(vert);
&#9;glDeleteShader(frag);

&#9;GLint linked = 0;
&#9;glGetProgramiv(g_program, GL_LINK_STATUS, &amp;linked);
&#9;if(!linked) {
&#9;&#9;char log[512];
&#9;&#9;glGetProgramInfoLog(g_program, sizeof(log), NULL, log);
&#9;&#9;fprintf(stderr, "sdl_gles2_jni: program link error: %s\n", log);
&#9;&#9;glDeleteProgram(g_program); g_program = 0;
&#9;&#9;SDL_GL_DeleteContext(g_context); g_context = NULL;
&#9;&#9;SDL_DestroyWindow(g_window);    g_window  = NULL;
&#9;&#9;SDL_Quit();
&#9;&#9;return JNI_FALSE;
&#9;}

&#9;g_posAttrib         = glGetAttribLocation(g_program,  "a_position");
&#9;g_colorUniform      = glGetUniformLocation(g_program, "u_color");
&#9;g_resolutionUniform = glGetUniformLocation(g_program, "u_resolution");

&#9;glUseProgram(g_program);
&#9;glUniform2f(g_resolutionUniform, (float)g_width, (float)g_height);
&#9;glViewport(0, 0, g_width, g_height);
&#9;return JNI_TRUE;
}

/* Java_susuwu_SdlGles2_destroy: tears down GLES2 + SDL2. */
JNIEXPORT void JNICALL Java_susuwu_SdlGles2_destroy(JNIEnv *env, jclass cls) {
&#9;if(g_program)  { glDeleteProgram(g_program);          g_program  = 0;    }
&#9;if(g_context)  { SDL_GL_DeleteContext(g_context);     g_context  = NULL; }
&#9;if(g_window)   { SDL_DestroyWindow(g_window);         g_window   = NULL; }
&#9;SDL_Quit();
}

/* Java_susuwu_SdlGles2_pollQuit: drains SDL event queue; returns JNI_TRUE if app should exit. */
JNIEXPORT jboolean JNICALL Java_susuwu_SdlGles2_pollQuit(JNIEnv *env, jclass cls) {
&#9;SDL_Event event;
&#9;while(SDL_PollEvent(&amp;event)) {
&#9;&#9;if(SDL_QUIT == event.type) {
&#9;&#9;&#9;return JNI_TRUE;
&#9;&#9;}
&#9;&#9;if(SDL_KEYDOWN == event.type &amp;&amp; SDLK_ESCAPE == event.key.keysym.sym) {
&#9;&#9;&#9;return JNI_TRUE;
&#9;&#9;}
&#9;}
&#9;return JNI_FALSE;
}

JNIEXPORT void JNICALL Java_susuwu_SdlGles2_glClearColor(JNIEnv *env, jclass cls, jfloat r, jfloat g, jfloat b, jfloat a) {
&#9;glClearColor(r, g, b, a);
}

JNIEXPORT void JNICALL Java_susuwu_SdlGles2_glClear(JNIEnv *env, jclass cls, jint mask) {
&#9;glClear((GLbitfield)mask);
}

JNIEXPORT void JNICALL Java_susuwu_SdlGles2_swapWindow(JNIEnv *env, jclass cls) {
&#9;SDL_GL_SwapWindow(g_window);
}

JNIEXPORT void JNICALL Java_susuwu_SdlGles2_setWindowTitle(JNIEnv *env, jclass cls, jstring jtitle) {
&#9;if(!g_window) { return; }
&#9;const char *title = (*env)-&gt;GetStringUTFChars(env, jtitle, NULL);
&#9;SDL_SetWindowTitle(g_window, title);
&#9;(*env)-&gt;ReleaseStringUTFChars(env, jtitle, title);
}

/* Java_susuwu_SdlGles2_drawFilledPolygon: draws pre-triangulated vertices (multiples of 3) in screen coords with solid color. */
JNIEXPORT void JNICALL Java_susuwu_SdlGles2_drawFilledPolygon(JNIEnv *env, jclass cls, jfloatArray jverts, jfloat r, jfloat g, jfloat b, jfloat a) {
&#9;jsize len   = (*env)-&gt;GetArrayLength(env, jverts);
&#9;jfloat *verts = (*env)-&gt;GetFloatArrayElements(env, jverts, NULL);

&#9;glUseProgram(g_program);
&#9;glUniform4f(g_colorUniform, r, g, b, a);
&#9;glVertexAttribPointer(g_posAttrib, 2, GL_FLOAT, GL_FALSE, 0, verts);
&#9;glEnableVertexAttribArray(g_posAttrib);
&#9;glDrawArrays(GL_TRIANGLES, 0, (GLsizei)(len / 2));
&#9;glDisableVertexAttribArray(g_posAttrib);

&#9;(*env)-&gt;ReleaseFloatArrayElements(env, jverts, verts, JNI_ABORT);
}</code></pre></div><div><hr></div><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/SdlGles2.java">./susuwu/SdlGles2.java</a>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;18575f3d-cd9e-4b5b-a4e8-ceff0f5a9ec1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; has the newest version of `./susuwu/SdlGles2.java` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

package susuwu; /* Usage: `import susuwu.SdlGles2;` */

/**
 * {@code class SdlGles2} is a thin JNI bridge to SDL2 and OpenGL ES 2.0.
 * Replaces JavaFX {@code Stage}, {@code Scene}, {@code Canvas}, {@code GraphicsContext}, {@code AnimationTimer}, {@code Timeline}.
 * Usage: {@code SdlGles2.init(width, height, title);} then render loop, then {@code SdlGles2.destroy();}
 */
public class SdlGles2 {
&#9;static {
&#9;&#9;System.loadLibrary("sdl_gles2_jni"); /* Loads `libsdl_gles2_jni.so` (or `.dll`/`.dylib`). Build: see `build.sh`. */
&#9;}

&#9;public static final int GL_COLOR_BUFFER_BIT = 0x00004000; /* Matches `GL_COLOR_BUFFER_BIT` from `&lt;GLES2/gl2.h&gt;` */

&#9;/** Creates the SDL2 window + OpenGL ES 2.0 context. Returns {@code true} on success. Replaces {@code Stage}, {@code Scene}. */
&#9;public static native boolean init(int width, int height, String title);
&#9;/** Destroys the SDL2 window + context. Calls {@code SDL_Quit()}. Replaces {@code stage.close()}. */
&#9;public static native void destroy();

&#9;/** Polls SDL events; returns {@code true} if SDL_QUIT or Escape was received (main loop should exit). Replaces {@code AnimationTimer}/{@code Timeline} termination. */
&#9;public static native boolean pollQuit();

&#9;/** Sets the GLES2 clear color. Replaces {@code Scene} background color. */
&#9;public static native void glClearColor(float r, float g, float b, float a);
&#9;/** Clears the GLES2 framebuffer. {@code mask} should be {@link #GL_COLOR_BUFFER_BIT}. Replaces {@code gc.clearRect()}. */
&#9;public static native void glClear(int mask);
&#9;/** Swaps front/back buffers (presents the rendered frame). Replaces implicit JavaFX frame commit. */
&#9;public static native void swapWindow();

&#9;/** Sets the window title. Used for FPS text display. Replaces {@code javafx.scene.text.Text}. */
&#9;public static native void setWindowTitle(String title);

&#9;/**
&#9; * Draws a filled polygon as triangles in screen space.
&#9; * {@code vertices}: interleaved {@code [x0,y0, x1,y1, ...]} in pixels (already transformed to screen coords).
&#9; * Vertex count must be a multiple of 3 (pre-triangulated input). Replaces {@code gc.beginPath/moveTo/lineTo/fill}.
&#9; */
&#9;public static native void drawFilledPolygon(float[] vertices, float r, float g, float b, float a);
}</code></pre></div><div><hr></div><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/build.sh">./build.sh</a>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;653d9a63-8fc7-4aff-a784-63a33a3fb42a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">#!/bin/sh
#
# /* This is the new build script for `./susuwu/FishSim.java`. */
# /* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
#  * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; has the newest version of `./build.sh` (henceforth "*this source code*").
#  * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
#  * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those. */

PATH_TO_CLASS="susuwu/FishSim"
PATH_TO_SOURCE="${PATH_TO_CLASS}.java"
PATH_TO_NATIVE="susuwu/sdl_gles2_jni.c"
PATH_TO_NATIVE_LIB="susuwu/libsdl_gles2_jni.so" # /* `.so` on Linux/Android, `.dll` on Windows, `.dylib` on macOS */
JAVA_FLAGS="${JAVA_FLAGS} -enableassertions" # /* Notice: remove `-enableassertions` so performance improves */
JAVA_FLAGS="${JAVA_FLAGS} -Djava.library.path=susuwu" # /* Allows JNI to find `libsdl_gles2_jni.so` */
export JAVA_BUILD_TEST_FLAGS="-verbose"
export JAVA_TEST_FLAGS="-verbose:module"
if command -v sudo &gt;/dev/null; then
&#9;APTITUDE="sudo apt -y install "
else
&#9;APTITUDE="apt -y install " # /* Fixes "The program sudo is not installed." on platforms such as smartphones */
fi
command -v java &gt;/dev/null || ${APTITUDE} openjdk-21-jdk-headless || ${APTITUDE} default-jdk-headless

if ! dpkg -l libsdl2-dev &gt;/dev/null 2&gt;&amp;1; then # /* Install SDL2 + GLES2 dev headers (replaces `openjfx`) */
&#9;${APTITUDE} libsdl2-dev libgles2-mesa-dev || true
fi

# /* Compile the JNI native library: `libsdl_gles2_jni.so` (replaces `--module-path`/`--add-modules javafx.*`) */
JAVA_HOME="${JAVA_HOME:-$(java -XshowSettings:properties -version 2&gt;&amp;1 | grep 'java.home' | sed 's/.*= //')}"
JNI_INCLUDES="-I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux" # /* Linux; macOS uses `include/darwin`, Android uses NDK paths */
#shellcheck disable=SC2086 # /* Quotes cause errors with pkg-config output */
cc -shared -fPIC "${PATH_TO_NATIVE}" -o "${PATH_TO_NATIVE_LIB}" ${JNI_INCLUDES} $(pkg-config --cflags --libs sdl2) -lGLESv2 || exit $?

if [ -n "${GITHUB_ACTIONS}" ]; then
#shellcheck disable=SC2086 # /* Quotes cause "Unrecognized option:" */
&#9;javac ${JAVA_BUILD_TEST_FLAGS} susuwu/SdlGles2.java susuwu/SimUsages.java ${PATH_TO_SOURCE} # /* Gives "Missing JavaFX application class susuwu/FishSim" unless `cd $(dirname ${PATH_TO_CLASS})` is used. */
else
#shellcheck disable=SC2086 # /* Quotes cause "Unrecognized option:" */
&#9;java ${JAVA_FLAGS} --source 16 ${PATH_TO_SOURCE} # /* `--source` is workaround for "error: cannot find symbol\n...\n  symbol: {class Force, variable Utils}" when not compiling all sources together */
fi

exit $? #Status required so [*CodeQL*](https://docs.github.com/en/code-security/code-scanning/introduction-to-code-scanning/about-code-scanning-with-codeql) passes.</code></pre></div><div><hr></div><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/SimUsages.java">./susuwu/SimUsages.java</a>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;a8b9bbbd-d66b-4460-964e-ca8741c7cc81&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; has the newest version of `./susuwu/SimUsages.java` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

package susuwu; /* Usage: `import susuwu.SimUsages;` */

/**
 * {@code class SimUsages} shows {@code FpsTextMode} statistics such as {@code fps} or {@code ms}.
 * Requirements: some render loop (for measurements). Is not specific to the renderer used.
 * Was produced for {@code susuwu.FishSim}, so the text (plus comments) assume the organisms are {@code class Fish}, but {@code SimUsages} is not specific to {@code class Fish}
 * Some {@code assert}s follow, thus document which arguments to use with this (without {@code -enableassertions}, thus are not enforced).
 * Some "Usage:" comments follow, which document how to use this.
 * Usage: {@code SimUsages usages = new SimUsages(); usages.show(); usages.fpsTextMode = FpsTextMode.fps.value | FpsTextMode.ms.value;}
 * Text is rendered via {@link SdlGles2#setWindowTitle} (replaces {@code javafx.scene.text.Text}).
 */
public class SimUsages {
&#9;/* `public` members */
&#9;public long fpsTextMode = FpsTextMode.allUsages.value; // Usage `usages.fpsTextModeFps = FpsTextMode.fps.value;` to just show `fps`
&#9;public double secondsPerFpsTextRefresh = 1.0; // Usage: `usages.secondsPerFpsTextRefresh = 0.2;` to give more current values, or `= 2.0;` to give more smooth values. In `postRefresh()`: if `secondsPerFpsTextRefresh` elapses, `lastTime = System.nanoTime(); fpsTextRefresh();``
&#9;public double renderMs = Double.NaN; // Usage: `functionWhichUsesRenderMs(usages.renderMs);`. Stores average **ms** from `startRender()` to `postRender()` (`renderNs / renderCounter / 1_000_000.0`)
&#9;public double physicsMs = Double.NaN; // Usage: `functionWhichUsesPhysicsMs(usages.physicsMs);`. Stores average **ms** from `startPhysics()` to `postPhysics()` (`physicsNs / physicsCounter / 1_000_000.0`)
&#9;public double fps = Double.NaN; // Usage: `functionWhichUsesFps(usages.fps);`. Stores `renderCounter / (System.nanoTime() - lastTime) / 1_000_000_000.0`

&#9;/* `private` or almost-`private` members */
&#9;public long lastTime = System.nanoTime(); // Stores `System.nanoTime()` when `renderCounter = 0`.
&#9;public int refreshCounter = 0; // Sum of `postRefresh()` uses since `SimUsages(Pane)`.
&#9;public int renderCounter = 0; // Sum of `postRender()` uses since `lastTime = System.nanoTime()`.
&#9;public int physicsCounter = 0; // Sum of `postPhysics()` uses since `lastTime = System.nanoTime()`.
&#9;public long physicsNs = -1; // Sum of `nanoTime()` at `postRender()` minus `nanoTime()` at `startRender()` since `lastTime = System.nanoTime()`.
&#9;public long renderNs = -1; // Sum of `nanoTime()` at `postRender()` minus `nanoTime()` at `startRender()` since `lastTime = System.nanoTime()`.
&#9;private String fpsText = "0 FPS";
&#9;public SimUsages() {
&#9;&#9;/* No Pane or display object needed: text is shown via SdlGles2.setWindowTitle(). */
&#9;}
&#9;static public enum FpsTextMode { // `FpsTextMode` says which resources `fpsText` will show.
&#9;&#9;none      (0     ), // `fpsText = "";`
&#9;&#9;fps       (1 &lt;&lt; 0), // `fpsText` += `fps` "FPS";
&#9;&#9;ms        (2 &lt;&lt; 1), // `fpsText` += `ms` "ms"; /* Notice: `ms = 1000 / fps;`, so includes idle CPU */
&#9;&#9;msSpec    (1 &lt;&lt; 2), // `fpsText` += `renderMs` "renderMs," `physicsMs` "physicsMs"; /* Notice: uses `System.nanoTime()`, does not include idle CPU */
&#9;&#9;msFish    (1 &lt;&lt; 3), // `fpsText` += `renderMs / fishShown` "renderMs / Fish shown," `physicsMs / fishListSize` "physicsMs / Fish";
&#9;&#9;fish      (1 &lt;&lt; 4), // `fpsText` += `fishListSize` "Fish";
&#9;&#9;fishShown (1 &lt;&lt; 5), // `fpsText` += `fishShown` "Fish shown";
&#9;&#9;allUsages (FpsTextMode.fps.value | FpsTextMode.ms.value | FpsTextMode.msSpec.value | FpsTextMode.msFish.value | FpsTextMode.fish.value | FpsTextMode.fishShown.value); // shows as all supported usages
&#9;&#9;long value; // Stores bitwise-or of those.
&#9;&#9;FpsTextMode(long value) { this.value = value; }
&#9;}; // TODO: replace manual bitshifts with `java.util.EnumSet&lt;E&gt;`?

&#9;public void show() {
&#9;&#9;SdlGles2.setWindowTitle("Fish Simulation (Boids) - " + fpsText); /* Replaces `Text.setX/Y/setFill(Color.WHITE)`: text is in window title. */
&#9;}

&#9;/* Measurement funtions
&#9; * Usage: ```
&#9; * refreshLoop() {
&#9; * &#9;usages.startRefresh();
&#9; * &#9;usages.startPhysics(); physics(fishList);   usages.postPhysics();
&#9; * &#9;usages.startRender();  render(visibleFish); usages.postRender();
&#9; * &#9;usages.postRefresh(System.nanoTime(), visibleFish.size(), fishList.size());
&#9; * }```
&#9; * "Refresh loop" is the loop which invokes the render loop plus the physics loop. `class SimUsages` does not assume that the physics loop is invoked as often as the render loop is. If the renderer has its own separate loop, the render loop is the refresh loop.
&#9; */
&#9;public void startRefresh() { // Usage: `startRender();` at start of refresh loop.
&#9;}
&#9;public void postRefresh(long now, long fishShown, long fishListSize) { // Usage: `postRefresh(System.nanoTime(), visibleObjects.size(), physisObjects.size());`
&#9;&#9;double elapsed = (now - lastTime) / 1_000_000_000.0;
&#9;&#9;if(elapsed &gt;= secondsPerFpsTextRefresh) {
&#9;&#9;&#9;lastTime = now;
&#9;&#9;&#9;fps = renderCounter / elapsed;
&#9;&#9;&#9;renderMs = renderNs / renderCounter / 1_000_000.0;
&#9;&#9;&#9;physicsMs = physicsNs / physicsCounter / 1_000_000.0;
&#9;&#9;&#9;fpsTextRefresh(fishShown, fishListSize); /* Replaces `Platform.runLater(...)`: SDL2 has no UI thread restriction, so call directly. */
&#9;&#9;&#9;renderCounter = 1;
&#9;&#9;&#9;renderNs = -1;
&#9;&#9;&#9;physicsCounter = 1;
&#9;&#9;&#9;physicsNs = -1;
&#9;&#9;}
&#9;&#9;refreshCounter++;
&#9;}
&#9;private long renderNsStart, physicsNsStart;
&#9;public void startRender() { // Usage: `startRender();` at start of render loop
&#9;&#9;renderNsStart = System.nanoTime();
&#9;}
&#9;public void preSynchro() { // Usage: `startRender(); ... startSynchro(); SdlGles2.glClear(...); postSynchro();`
&#9;&#9;renderNsStart += System.nanoTime();
&#9;} // TODO: reduce `preSynchro()` to no-op, improve `postSynchro()` to subtract actual Virtual Synchronization time from `renderNsStat` (so the time to clear buffers is included).
&#9;public void postSynchro() { // Usage: `startRender(); ... startSynchro(); SdlGles2.glClear(...); postSynchro();`
&#9;&#9;renderNsStart -= System.nanoTime(); // Purpose: subtracts vertical synchronization time from "drawMS` (which is computed from `renderNs`, which is computed from `renderNsStart`). Problem: `glClear()` does not just wait for Virtical Synchronization, but also clears the buffer, which this will also subtract from `renderNs`.
&#9;}
&#9;public void postRender() { // Usage: `startRender();` at closure of render loop
&#9;&#9;renderNs += System.nanoTime() - renderNsStart;
&#9;&#9;renderCounter++;
&#9;}
&#9;public void startPhysics() { // Usage: `startPhysics();` at start of physics loop
&#9;&#9;physicsNsStart = System.nanoTime();
&#9;}
&#9;public void postPhysics() { // Usage: `startPhysics();` at closure of physics loop
&#9;&#9;physicsNs += System.nanoTime() - physicsNsStart;
&#9;&#9;physicsCounter++;
&#9;}

&#9;private void fpsTextRefresh(long fishShown, long fishListSize) { /* Usage: `usages.fpsTextModeFps(visibleFish.size(), fishList.size())` */
&#9;&#9;boolean fpsTextModeFps = (0 != (FpsTextMode.fps.value &amp; fpsTextMode));
&#9;&#9;boolean fpsTextModeMs = (0 != (FpsTextMode.ms.value &amp; fpsTextMode));
&#9;&#9;boolean fpsTextModeMsSpec = (0 != (FpsTextMode.msSpec.value &amp; fpsTextMode));
&#9;&#9;boolean fpsTextMsSpecFish = (0 != ((FpsTextMode.msSpec.value | FpsTextMode.msFish.value) &amp; fpsTextMode)); // TODO: replace manual bitshifts with `java.util.EnumSet&lt;E&gt;`?
&#9;&#9;boolean fpsTextModeMsFish = (0 != (FpsTextMode.msFish.value &amp; fpsTextMode));
&#9;&#9;boolean fpsTextModeFish = (0 != (FpsTextMode.fish.value &amp; fpsTextMode));
&#9;&#9;boolean fpsTextModeFishShown = (0 != (FpsTextMode.fishShown.value &amp; fpsTextMode));
&#9;&#9;double totalMs = 1 / fps * 1000;
&#9;&#9;String fpsTextStr = "";
&#9;&#9;String strSep = ", ", strJoin = " (";
&#9;&#9;if(FpsTextMode.none.value == fpsTextMode) { return; }
&#9;&#9;if(fpsTextModeFps) {
&#9;&#9;&#9;fpsTextStr += String.format("%4.2f FPS" + strSep, fps);
&#9;&#9;}
&#9;&#9;if(fpsTextModeMs) {
&#9;&#9;&#9;fpsTextStr += String.format("%4.2f MS" + (fpsTextMsSpecFish ? strJoin : strSep), totalMs);
&#9;&#9;}
&#9;&#9;if(fpsTextModeMsSpec) {
&#9;&#9;&#9;fpsTextStr += String.format("%4.2f drawMS, %4.2f physicsMS", renderMs, physicsMs);
&#9;&#9;&#9;fpsTextStr += (fpsTextModeMsFish ? strSep : "");
&#9;&#9;}
&#9;&#9;if(fpsTextModeMsFish) {
&#9;&#9;&#9;fpsTextStr += String.format("%2.4f drawMS / Fish shown, %2.4f physicsMS / Fish", renderMs / fishShown, physicsMs / fishListSize);
&#9;&#9;}
&#9;&#9;if(fpsTextMsSpecFish) {
&#9;&#9;&#9;if(0 != ((FpsTextMode.ms.value &amp; fpsTextMode))) {
&#9;&#9;&#9;&#9;fpsTextStr += ")";
&#9;&#9;&#9;}
&#9;&#9;&#9;fpsTextStr += strSep;
&#9;&#9;}
&#9;&#9;if(fpsTextModeFish) {
&#9;&#9;&#9;fpsTextStr += String.format("%4d Fish", fishListSize);
&#9;&#9;&#9;fpsTextStr += (fpsTextModeFishShown ? strJoin : strSep);
&#9;&#9;}
&#9;&#9;if(fpsTextModeFishShown) {
&#9;&#9;&#9;fpsTextStr += String.format(fpsTextModeFish ? "%4d shown)" : "%4d Fish shown", fishShown);
&#9;&#9;&#9;fpsTextStr += strSep;
&#9;&#9;}
&#9;&#9;fpsText = fpsTextStr.substring(0, fpsTextStr.length() - strSep.length());
&#9;&#9;SdlGles2.setWindowTitle("Fish Simulation (Boids) - " + fpsText); /* Replaces `Text.setText(...)`: update window title with FPS stats. */
&#9;}
};</code></pre></div><div><hr></div><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/copilot/replace-javafx-with-sdl2-opengles/susuwu/FishSim.java">./susuwu/FishSim.java</a>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;fb401aaf-d38f-4c7c-a49a-0b7bae57635c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; has the newest version of `./susuwu/FishSim.java` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

package susuwu; /* Usage: `import susuwu.FishSim;` */

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import susuwu.SimUsages; /* `class SimUsages`, `enum FpsTextMode` */
import susuwu.SdlGles2; /* `class SdlGles2`: JNI bridge to SDL2 + GLES2 */
import susuwu.Calculus; /* `Calculus.pow2()` */
import susuwu.Forces; /* `class Forces implements java.lang.Cloneable` */
import susuwu.ImmutablePosBounds; /* `enum PosBoundsMode`: which stores how sims enforce bounds. */
import susuwu.PosBounds; /* `class PosBounds : extends ImmutablePosBounds`, `PosBounds.set*(PosBounds*)` */
import susuwu.ImmutablePos; /* `class ImmutablePos implements java.util.RandomAccess` */
import susuwu.Pos; /* `class Pos extends ImmutablePos` */
import susuwu.Pos2; /* `class Pos extends Pos` */
//    public static class Pos2 extends double[2] {} // `{Pos2[0], Pos2[1]}` is `{x, y}` position (or resolution), or is `{pos[0], pos[0]}` motion (derivative of position), or is is `{d2x, d2y}` acceleration (derivative number 2). This was supposed to do what `typedef` does (wish for future-proof (limitless dimensions) virtual `class` with functions for numerous transforms).
// TODO: test how much of `java`'s [static `Array` overhead](https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/Physics_sims_which_structures_to_use.md#separate-variables-versus-dim-lists) `java`'s toolkit optimizes for you. If performance is a problem, choose a new approach to use.

/**
 * Simple SDL2+GLES2 fish sim (via JNI), which includes reusable {@code public class}s (for new sims to use). Most of the reusable {@code public class}s are in other {@code .java} sources for {@code package susuwu}
 * This ([`./susuwu/FishSim.java`](./FishSim.java)) uses pseudo-*Markdown* for comments, but [`./posts/FishSim.md`](../posts/FishSim.md) is the actual [*Markdown*](https://github.github.com/gfm/) document for this.
 * Notice: replaced most of [*Solar-Pro-2*'s original `FishSim.java`](https://github.com/SwuduSusuwu/SusuJava/blob/solarPro2FishSim/susuwu/FishSim.java), as [`./posts/FishSim.md#intro`](../posts/FishSim.md#intro) documents (plus [*GitHub*'s `/compare/` tool shows](https://github.com/SwuduSusuwu/SusuJava/compare/solarPro2FishSim..susuFishSim#diff-8c440bb92bc6939e1450542897e0bbb1a8737b93808ea63ed32784edfacef4b4).
 */
public class FishSim {
&#9;/** Minimal {@code Color} replacement (replaces {@code javafx.scene.paint.Color}). Supports {@code getRed()}, {@code getGreen()}, {@code getBlue()} for {@code isSimilarTo()} comparisons. */
&#9;public static class Color {
&#9;&#9;private final double red, green, blue;
&#9;&#9;private Color(double r, double g, double b) { this.red = r; this.green = g; this.blue = b; }
&#9;&#9;public static Color color(double r, double g, double b) { return new Color(r, g, b); }
&#9;&#9;public double getRed()   { return red; }
&#9;&#9;public double getGreen() { return green; }
&#9;&#9;public double getBlue()  { return blue; }
&#9;&#9;public static final Color LIGHTBLUE = new Color(0.678, 0.847, 0.902); /* Light blue background (replaces `Color.LIGHTBLUE` from JavaFX) */
&#9;}

&#9;public enum PhysicsMode { // `PhysicsMode` says how to execute `updateFish()`
&#9;&#9;synchronousHomo,      // `updateFish()` once per `refreshLoop()`.
&#9;&#9;synchronousInterval,  // `updateFish()` per `positionInterval` `refreshLoop()`s.
&#9;&#9;asynchronousHomo,     // `executor.submit(() -&gt; updateFish());` once per `refreshLoop()`.
&#9;&#9;asynchronousInterval, // `executor.submit(() -&gt; updateFish());` per `positionInterval` `refreshLoop()`s.
&#9;&#9;separateUnbound,      // `updateFish()` runs in a background thread continuously (replaces `AnimationTimer`). Notice: with `GLES2` this has an implicit bound to the monitor refresh (Virtual Synchronization, which `SimUsages` does not count towards "drawMS").
&#9;&#9;separateFps,          // `updateFish()` runs via `ScheduledExecutorService` at `physicsRefreshHertz` (replaces `Timeline/KeyFrame`).
&#9;}
&#9;private static PhysicsMode monitorRefreshMode = PhysicsMode.separateFps; // `monitorRefreshMode` must use `.separateUnbound` or `.separateFps`.
&#9;private static PhysicsMode physicsMode = PhysicsMode.separateFps; // Notice: if `PhysicsMode.*Interval`, must set `positionInterval`. if `PhysicsMode.separateFps`, must set `physicsRefreshHertz`.

&#9;static ReentrantLock renderFishLock = new ReentrantLock();
&#9;static ReentrantLock updateFishLock = new ReentrantLock();
&#9;// TODO: remove `static` from {`resolution`, `bounds`}, to allow to remove `static` from {`setResolution()`, `renderFishLock, `updateFishLock`}, so `FishSim` allows numerous windows
&#9;public static boolean setResolution(int[] newResolution) { /* Notice: invalidates references to old `bounds`, `*Slash2` addresses. */ // TODO: remove `static` from {`resolution`, `bounds`}, to allow to remove `static` from {`setResolution()`, `renderFishLock, `updateFishLock`}, so `FishSim` allows numerous windows
&#9;&#9;assert 2 == newResolution.length;
&#9;&#9;assert 0 &lt; newResolution[0]; //TODO: allow "headless" instances with `resolution = {0, 0}`?
&#9;&#9;assert 0 &lt; newResolution[1];
&#9;&#9;updateFishLock.lock();
&#9;&#9;renderFishLock.lock();
&#9;&#9;resolution = newResolution;
&#9;&#9;resolutionf.pos[0] = resolution[0]; resolutionf.pos[1] = resolution[1];
&#9;&#9;resolutionfSlash2 = resolutionf.slashScalar(2); /* Notice: invalidates references which store the old address to `resolutionfSlash2`. */
&#9;&#9;resVolume = (int)Math.round(resolutionf.volume()); /* Notice: uses `Pos::volume()` since simple source code is less bug prone. `Math.round` ensures 24-bit mantissas give accurate values */
&#9;&#9;posBounds.setBounds(resolutionf.starScalar(boundsResolutionFactor).pos); // TODO: if sure that no functions store references to the original instance, replace the above row with this (since simple source code is less bug prone)
&#9;&#9;// (Resize SDL window here if needed: SDL_SetWindowSize(window, resolution[0], resolution[1]))
&#9;&#9;// (Resize GLES2 viewport here if needed: glViewport(0, 0, resolution[0], resolution[1]))
&#9;&#9;// (Rebuild GLES2 u_resolution uniform: SdlGles2.glClearColor/etc. if resolution changes)
&#9;&#9;renderFishLock.unlock();
&#9;&#9;updateFishLock.unlock();
&#9;&#9;return true;
&#9;}
&#9;private static int[] resolution = {1280, 720};
&#9;private static Pos2 resolutionf = new Pos2(resolution[0], resolution[1]);
&#9;private static Pos resolutionfSlash2 = resolutionf.slashScalar(2); /* Improves execution of inner loops which use this. Notice: `setResolution(newResolution)` invalidates stored references to `resolutionfSlash2` */
&#9;private static int resVolume = (int)Math.round(resolutionf.volume()); /* Notice: uses `Pos::volume()` since simple source code is less bug prone. `Math.round` ensures 24-bit mantissas give accurate values */
&#9;private static double boundsResolutionFactor = (1_000_000 &gt; resVolume ? 2.0 : 1.2); // Ocean is `resolution[dim] * boundsResolutionFactor`. `2.0` gives more room for natural oceans, but old laptops with huge resolutions (such as `{2200, 1200}`) must use `1.2` so the load is low enough for old CPUs to process). TODO: include short benchmark (on startup) to set `boundsResolutionFactor` to optimal value, or reduce CPU use for unshown `Fish` (`if(!fish.isVisible)`, then execute `updateFish` just once per second (1 hertz), with larger steps).
&#9;private static PosBounds posBounds = new PosBounds(PosBounds.PosBoundsMode.wrapAroundResolution, resolutionf.starScalar(boundsResolutionFactor)); // for simple sims, use `resolutionf.clone()`
&#9;private static double fishVolume = 200; // Uses resolution of `Fish::render()`.
&#9;private static double fishLengthsSep = 62; // Average `Fish`-lengths distance  from `Fish` to `Fish`.
&#9;private static double fishPerVolume = 1 / fishVolume / fishLengthsSep; // `Fish` per volume (for 2D, volume is resolution).
&#9;private static int fishCount = (int)(posBounds.getBoundsVolume() * fishPerVolume);
&#9;private static int gridResolution = 100; // Notice: set this to `Colllections.max({forces*.distance})` (which should equal what most sims call "view distance"), so that all relevent `Fish` are processed.
&#9;private static int positionInterval = 2; // The `refreshCounter` per `Fish::applyFlockingRulesUpdate()`
&#9;public static double monitorRefreshHertz = 60.0; // The `SimUsages.fps` to wish for // Notice: since this limits `SimUsages.fps` to `monitorRefreshHertz`, this prevents benchmarks which use `FpsTextMode.fps` (or `FpsTextMode.ms`). Benchmarks can still use `FpsTextMode.msSpec` (or `FpsTextMode.msFish`).
&#9;public static double physicsRefreshHertz = monitorRefreshHertz / positionInterval; // The `1 / SimUsages.physicsMs` to wish for // Notice: unknown what `javafx.animation.Timeline` does if `physicsRefreshHertz &gt; (1 / SimUsages.physicsMs)`, but guess thus stalls or consumes multiple executors

&#9;private List&lt;Fish&gt; fishList = new ArrayList&lt;&gt;();
&#9;private int fishShown = 0;
&#9;private List&lt;Fish&gt;[][] grid; /* `listToPartitions(List&lt;&gt;[][] grid, List&lt;&gt; list)` uses this */
&#9;private Random random = new Random();
&#9;SimUsages simUsages = new SimUsages(); /* Replaces `new SimUsages(root)`: no Pane needed for SDL2 text (shown via window title). */
//&#9;simUsages.fpsTextMode = FpsTextMode.allUsages.value; // TODO: "error: &lt;identifier&gt; expected" solution

&#9;private volatile boolean quit = false; /* Set to `true` by `stop()` to signal the main SDL loop to exit. */
&#9;private ExecutorService executor = Executors.newSingleThreadExecutor();
&#9;private ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); /* Replaces `javafx.animation.Timeline` for `separateFps` physics. */

&#9;public static void main(String[] args) {
&#9;&#9;new FishSim().run(args); /* Replaces `launch(args)`: instantiate directly since there is no JavaFX Application lifecycle. */
&#9;}

&#9;/** Initializes SDL2+GLES2, populates fish, starts physics loops, then runs the render loop until quit. Replaces {@code start(Stage primaryStage)}. */
&#9;public void run(String[] args) { /* `args` preserved for future CLI configuration (e.g., `--resolution`, `--physics-mode`); currently unused. */
&#9;&#9;if(!SdlGles2.init(resolution[0], resolution[1], "Fish Simulation (Boids)")) {
&#9;&#9;&#9;System.err.println("FishSim.run: SdlGles2.init failed; aborting.");
&#9;&#9;&#9;return;
&#9;&#9;}
&#9;&#9;SdlGles2.glClearColor( /* Light-blue background (replaces `Color.LIGHTBLUE` passed to `new Scene(...)`) */
&#9;&#9;&#9;(float)Color.LIGHTBLUE.getRed(), (float)Color.LIGHTBLUE.getGreen(), (float)Color.LIGHTBLUE.getBlue(), 1.0f);

&#9;&#9;// Initialize fish
&#9;&#9;for(int i = 0; i &lt; fishCount; i++) {
&#9;&#9;&#9;Pos2 pos = new Pos2(random.nextDouble() * posBounds.getBounds(0), random.nextDouble() * posBounds.getBounds(1));
&#9;&#9;&#9;Pos2 dpos = new Pos2((random.nextDouble() * 2 - 1) * Fish.dposMax, (random.nextDouble() * 2 - 1) * Fish.dposMax);
&#9;&#9;&#9;fishList.add(new Fish(pos, dpos, Color.color(random.nextDouble(), random.nextDouble(), random.nextDouble())));
&#9;&#9;}

&#9;&#9;posBounds.setGridResolution(gridResolution);
&#9;&#9;grid = new ArrayList[posBounds.getGridSize(0)][posBounds.getGridSize(1)]; /* `listToPartitions(List&lt;&gt;[][] grid, List&lt;&gt; list)` uses this */
&#9;&#9;for(int i = 0; i &lt; grid.length; i++) {
&#9;&#9;&#9;for(int j = 0; j &lt; grid[i].length; j++) {
&#9;&#9;&#9;&#9;grid[i][j] = new ArrayList&lt;&gt;();
&#9;&#9;&#9;}
&#9;&#9;}

&#9;&#9;simUsages.show();

&#9;&#9;// Start separate physics loop for `separateUnbound` / `separateFps` modes (replaces `AnimationTimer` / `Timeline`):
&#9;&#9;switch(physicsMode) { // `PhysicsMode.` is omitted from all `case`s, to support old `java --source` versions
&#9;&#9;case separateUnbound:
&#9;&#9;&#9;executor.submit(() -&gt; { while(!quit) { updateFish(); } });
&#9;&#9;&#9;break;
&#9;&#9;case separateFps:
&#9;&#9;&#9;long physicsIntervalMs = Math.max(1L, (long)(1_000.0 / physicsRefreshHertz)); /* Use milliseconds for scheduler precision (avoids nanosecond scheduler overhead). */
&#9;&#9;&#9;scheduledExecutor.scheduleAtFixedRate(() -&gt; updateFish(), 0, physicsIntervalMs, TimeUnit.MILLISECONDS);
&#9;&#9;&#9;break;
&#9;&#9;default:
&#9;&#9;&#9;break; /* synchronous* / asynchronous* modes: handled inside `refreshLoop()` */
&#9;&#9;}

&#9;&#9;// Main SDL render loop (replaces `AnimationTimer` / `Timeline` for `monitorRefreshMode`):
&#9;&#9;long renderIntervalNs = (long)(1_000_000_000.0 / monitorRefreshHertz);
&#9;&#9;long lastRenderTime = System.nanoTime();
&#9;&#9;while(!quit &amp;&amp; !SdlGles2.pollQuit()) {
&#9;&#9;&#9;long now = System.nanoTime();
&#9;&#9;&#9;boolean shouldRender;
&#9;&#9;&#9;switch(monitorRefreshMode) { // `PhysicsMode.` is omitted from all `case`s, to support old `java --source` versions
&#9;&#9;&#9;case separateUnbound: // Notice: uses Vertical Synchronization, which `SimUsages` subtracts from `renderNs` (does not count towards resource usage).
&#9;&#9;&#9;&#9;shouldRender = true;
&#9;&#9;&#9;&#9;break;
&#9;&#9;&#9;case separateFps: /* fall-through */
&#9;&#9;&#9;default:
&#9;&#9;&#9;&#9;shouldRender = ((now - lastRenderTime) &gt;= renderIntervalNs);
&#9;&#9;&#9;&#9;break;
&#9;&#9;&#9;}
&#9;&#9;&#9;if(shouldRender) {
&#9;&#9;&#9;&#9;refreshLoop(now);
&#9;&#9;&#9;&#9;lastRenderTime = now;
&#9;&#9;&#9;} else {
&#9;&#9;&#9;&#9;try { Thread.sleep(1); } catch(InterruptedException e) { Thread.currentThread().interrupt(); break; } /* 1ms sleep avoids busy-waiting while still responding within 1 frame at 60fps (~16ms). */
&#9;&#9;&#9;}
&#9;&#9;}
&#9;&#9;stop();
&#9;}

&#9;private void refreshLoop(long now) {
&#9;&#9;simUsages.startRefresh();
&#9;&#9;switch(physicsMode) { // `PhysicsMode.` is omitted from all `case`s, to support old `java --source` versions
&#9;&#9;case synchronousHomo:
&#9;&#9;&#9;updateFish();
&#9;&#9;&#9;break;
&#9;&#9;case synchronousInterval:
&#9;&#9;&#9;if(simUsages.refreshCounter % positionInterval == 0) {
&#9;&#9;&#9;&#9;updateFish();
&#9;&#9;&#9;}
&#9;&#9;&#9;break;
&#9;&#9;case asynchronousHomo:
&#9;&#9;&#9;executor.submit(() -&gt; updateFish());
&#9;&#9;&#9;break;
&#9;&#9;case asynchronousInterval:
&#9;&#9;&#9;if(simUsages.refreshCounter % positionInterval == 0) {
&#9;&#9;&#9;&#9;executor.submit(() -&gt; updateFish());
&#9;&#9;&#9;}
&#9;&#9;&#9;break;
&#9;&#9;case separateUnbound:
&#9;&#9;case separateFps:
&#9;&#9;&#9;break; // no-op for both, since `run()` processes thus
&#9;&#9;default:
&#9;&#9;&#9;throw new IllegalArgumentException("Unsupported `PhysicsMode physicsMode`: " + physicsMode);
&#9;&#9;}

&#9;&#9;renderFish();
&#9;&#9;simUsages.postRefresh(now, fishShown, fishList.size());
&#9;}

&#9;private void outOfBounds(String function, Fish fish) {
&#9;&#9;/* Notice: `outOfBounds()` has numerous sensible actions other than to print to `stderr`: `fish.die()`, `fish.stop()`, `fish.reverse()`, `fish.wrapAround()` */
&#9;&#9;System.err.println(function + ": " + posBounds.posOutOfBoundsStr(fish.pos, "Fish.pos"));
&#9;}

&#9;/* Spatial partitioning (simple grid system). TODO: generic version of this (accept all `class`s with `#isInBounds` plus `#pos`). */
&#9;private void listToPartitions(List&lt;Fish&gt;[][] grid, List&lt;Fish&gt; list) {
&#9;&#9;assert grid.length == (int)Math.ceil(posBounds.getBounds(0) / gridResolution);
&#9;&#9;assert grid[0].length == (int)Math.ceil(posBounds.getBounds(1) / gridResolution);
&#9;&#9;for(int i = 0; i &lt; grid.length; i++) {
&#9;&#9;&#9;for(int j = 0; j &lt; grid[i].length; j++) {
&#9;&#9;&#9;&#9;grid[i][j].clear();
&#9;&#9;&#9;}
&#9;&#9;}
&#9;&#9;for(Fish fish : list) { /* Assign list members to grid sections */
&#9;&#9;&#9;if(fish.isInBounds) {
&#9;&#9;&#9;&#9;int[] gridPos = {(int) (fish.pos.pos[0] / gridResolution), (int) (fish.pos.pos[1] / gridResolution)};
&#9;&#9;&#9;&#9;grid[gridPos[0]][gridPos[1]].add(fish); // if `gridPos` is not in bounds, this will `throw new IndexOutOfBoundsException()`. But `Fish.setPos()` uses `PosBounds::posBounds.posBound()` which, which ensures `Fish.pos` bounds to `FishSim.resolution`, so this will not `throw`.
&#9;&#9;&#9;}
&#9;&#9;}
&#9;}

&#9;private void updateFish() {
&#9;&#9;updateFishLock.lock();
&#9;&#9;simUsages.startPhysics();

&#9;&#9;listToPartitions(grid, fishList);

&#9;&#9;// Update each fish
&#9;&#9;for(Fish fish : fishList) {
&#9;&#9;&#9;fish.applyFlockingRules(fishList, grid);
&#9;&#9;&#9;fish.update();
&#9;&#9;}

&#9;&#9;simUsages.postPhysics();
&#9;&#9;updateFishLock.unlock();
&#9;}

&#9;private void renderFish() {
&#9;&#9;renderFishLock.lock();
&#9;&#9;simUsages.startRender();
&#9;&#9;fishShown = 0;
&#9;&#9;simUsages.preSynchro(); // Notice: alternatives: use `SDL_GL_SetSwapInterval(0);`, or move `simUsages.startRender()` below the first use of `SdlGles2`
&#9;&#9;SdlGles2.glClear(SdlGles2.GL_COLOR_BUFFER_BIT); // Replaces `gc.clearRect(0, 0, resolution[0], resolution[1])`
&#9;&#9;simUsages.postSynchro();
&#9;&#9;for(Fish fish : fishList) {
&#9;&#9;&#9;if(fish.isVisible) { // For `Fish` not shown, this condition improves `SimUsages.fps` (lowers `SimUsages.renderNs`).
&#9;&#9;&#9;&#9;fishShown++;
&#9;&#9;&#9;&#9;fish.render();
&#9;&#9;&#9;}
&#9;&#9;}
&#9;&#9;SdlGles2.swapWindow(); // Presents the rendered frame (replaces implicit JavaFX frame commit).
&#9;&#9;simUsages.postRender();
&#9;&#9;renderFishLock.unlock();
&#9;}

&#9;/** Signals the main loop to exit, shuts down executor threads, and calls {@code SDL_Quit()} via {@link SdlGles2#destroy()}. Replaces {@code @Override stop()}. */
&#9;public void stop() {
&#9;&#9;quit = true;
&#9;&#9;scheduledExecutor.shutdownNow();
&#9;&#9;executor.shutdown();
&#9;&#9;SdlGles2.destroy(); /* Replaces implicit JavaFX window teardown. */
&#9;}

&#9;public class Fish { /* `static Fish` causes "{posBounds,posBounds.posBound()} cannot be referenced from a static context" (unless those are set to `static`, which prevents `FishSim` from use of separate values with multiple windows) */
&#9;&#9;public static Forces forcesSeparation = new Forces(22.0, 2.0);
&#9;&#9;public static Forces forcesSeparationNonsimilar = new Forces(100.0, 2.2);
&#9;&#9;public static Forces forcesAlignment = new Forces(100.0, 1.0);
&#9;&#9;public static Forces forcesCohesion = new Forces(100.0, 1.0);
&#9;&#9;public static Forces forcesBounds = new Forces(100.0, 2.0);
&#9;&#9;private static double dposMax = 3.0;   // Motion lim (limit of derivative of position)
&#9;&#9;private static double d2Pos = 0.1;     // Motion&lt;sup&gt;2&lt;/sup&gt; (derivative #2 of position)
&#9;&#9;private static double isSimilarTolerance = 0.2;
&#9;&#9;public static boolean applyWallAvoidanceTru = (PosBounds.PosBoundsMode.wrapAroundResolution != posBounds.getPosBoundsMode());
&#9;&#9;public static boolean redFishAreAggressiveOrPoisonous = true; // changes how `isSimilarTo(Fish other)` uses `color.getRed()`

&#9;&#9;private Pos pos;        // Position
&#9;&#9;private Pos dpos;       // Motion (derivative of position)
&#9;&#9;private Color color;    /* Replaces `javafx.scene.paint.Color`: uses `FishSim.Color` which supports `getRed()`, `getGreen()`, `getBlue()`. */
&#9;&#9;public boolean isInBounds;
&#9;&#9;public boolean isVisible = false; // Just stores `0 &lt;= pos[0] &amp;&amp; resolution[0] &gt; pos[0] &amp;&amp; 0 &lt;= pos[1]  &amp;&amp; resolution[1] &gt; pos[1]` for now.

&#9;&#9;public Fish(ImmutablePos pos, ImmutablePos dpos, Color color) {
&#9;&#9;&#9;this.pos = pos.clone(); // TODO: ensure this clones the actual (specialized) virtual function addresses, which improve CPU use
&#9;&#9;&#9;this.dpos = dpos.clone();
&#9;&#9;&#9;this.color = color;
&#9;&#9;}
&#9;&#9;public Fish(Pos pos, Pos dpos, Color color) { /* Notice: uses "placement moves" for {`pos`, `dpos`}. Gives `Fish` ownership of `pos`, ownership of `dpos`. */
&#9;&#9;&#9;this.pos = pos;
&#9;&#9;&#9;this.dpos = dpos;
&#9;&#9;&#9;this.color = color;
&#9;&#9;}

&#9;&#9;public boolean isSimilarTo(Fish o) {
//&#9;&#9;&#9;return color.equals(o.color); // Less CPU use, but requires that `fishList` has just a few colors.
//&#9;&#9;&#9;Color colorDis = Color.color(color.getRed() - o.color.getRed(), color.getGreen() - o.color.getGreen(), color.getBlue() - o.color.getBlue()); // Notice: `Color` is more intuitive to use, but was concerned that `java` will not fold this
&#9;&#9;&#9;double[] colorDis = {color.getRed() - o.color.getRed(), color.getGreen() - o.color.getGreen(), color.getBlue() - o.color.getBlue()};
&#9;&#9;&#9;if(redFishAreAggressiveOrPoisonous) {
&#9;&#9;&#9;&#9;colorDis[0] = Calculus.pow2(colorDis[0]);
&#9;&#9;&#9;}
//&#9;&#9;&#9;return isSimilarTolerance &gt; (Math.hypot(Math.abs(colorDis[0]), Math.abs(colorDis[1]), Math.abs(colorDis[2]))); // [`Math.hypot()` still does not support &gt; 2 dimensions?](https://esdiscuss.org/topic/how-about-more-args-for-math-hypot). Notice: if you use Euclidean distance, lower `isSimilarTolerance`.
&#9;&#9;&#9;return isSimilarTolerance &gt; (Calculus.pow2(colorDis[0]) + Calculus.pow2(colorDis[1]) + Calculus.pow2(colorDis[2]));
&#9;&#9;} // TODO: Use a function (such as `javafx.scene.shape.Polygon.getPoints()`) for comparison of vertices. */

&#9;&#9;public Pos getPosDiff(Fish o) {
&#9;&#9;&#9;return posBounds.posDiff(pos, o.pos);
&#9;&#9;}

&#9;&#9;public synchronized void setPos(Pos newPos /* Notice: semantics of "placement move" */) {
&#9;&#9;&#9;isInBounds = posBounds.posBound(newPos); // If `PosBoundsMode.boundless != posBounds.getPosBoundsMode()`, this ensures the invariant `0 &lt;= pos[dim] &amp;&amp; PosBounds.getBounds()[dim] &gt; pos[dim]` is established.
&#9;&#9;&#9;isVisible = (0 &lt;= newPos.pos[0] &amp;&amp; resolution[0] &gt; newPos.pos[0] &amp;&amp; 0 &lt;= newPos.pos[1]  &amp;&amp; resolution[1] &gt; newPos.pos[1]); // TODO: +`Pos::isLessOrEquals(Pos)`, +`Pos::isMore(pos)`
&#9;&#9;&#9;if((!isInBounds) &amp;&amp; PosBounds.PosBoundsMode.boundless != posBounds.getPosBoundsMode()) {
&#9;&#9;&#9;&#9;outOfBounds("Fish::setPos", this);
&#9;&#9;&#9;}
&#9;&#9;&#9;pos = newPos; /* Notice: does not use `newPos.clone()` since this function is used in inner loops. After this function returns, `this` has ownership of `newPos`. */
&#9;&#9;}
&#9;&#9;public synchronized void setPos(ImmutablePos newPos) {
&#9;&#9;&#9;setPos(newPos.clone()); /* Notice: since this function is used in inner loops, `setPos(Pos newPos)` uses the semantics of "placement move", so if `newPos` is immutable, must clone. */
&#9;&#9;}

&#9;&#9;public synchronized void applyFlockingRules(List&lt;Fish&gt; allFish, List&lt;Fish&gt;[][] grid) {
&#9;&#9;&#9;int[] gridPos = {(int) (pos.pos[0] / gridResolution), (int) (pos.pos[1] / gridResolution)};
&#9;&#9;&#9;List&lt;Fish&gt; nearbyFish = new ArrayList&lt;&gt;();

&#9;&#9;&#9;// Check neighboring grid cells
&#9;&#9;&#9;if(PosBounds.PosBoundsMode.wrapAroundResolution == posBounds.getPosBoundsMode()) {
&#9;&#9;&#9;&#9;for(int i = gridPos[0] - 1; i &lt;= gridPos[0] + 1; i++) {
&#9;&#9;&#9;&#9;&#9;for(int j = gridPos[1] - 1; j &lt;= gridPos[1] + 1; j++) {
&#9;&#9;&#9;&#9;&#9;&#9;nearbyFish.addAll(grid[(i + grid.length) % grid.length][(j + grid[0].length) % grid[0].length]);
&#9;&#9;&#9;&#9;&#9;} /* TODO: move expensive `%`s into outer loop somehow. With `1000 == fishList.size() &amp;&amp; 100 == monitorRefreshHertz`, 2 `%`s in inner loop executes 200,000 `%`/s. The most simple solution is to use outer branches to choose from loops which hardcode this, but thus duplicates codeflow. Does `java` do this for you if the loop uses most of the CPU? */
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;} else {
&#9;&#9;&#9;&#9;for(int i = Math.max(0, gridPos[0] - 1); i &lt;= Math.min(grid.length - 1, gridPos[0] + 1); i++) {
&#9;&#9;&#9;&#9;&#9;for(int j = Math.max(0, gridPos[1] - 1); j &lt;= Math.min(grid[0].length - 1, gridPos[1] + 1); j++) {
&#9;&#9;&#9;&#9;&#9;&#9;nearbyFish.addAll(grid[i][j]);
&#9;&#9;&#9;&#9;&#9;} /* TODO: move expensive `Math.{min,max}`s into outer loop somehow. With `1000 == fishList.size() &amp;&amp; 100 == monitorRefreshHertz`, inner loop executes 200,000 `Math.{min,max}`/s */
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;}

&#9;&#9;&#9;applySeparation(nearbyFish);
&#9;&#9;&#9;applyAlignment(nearbyFish);
&#9;&#9;&#9;applyCohesion(nearbyFish);
&#9;&#9;&#9;if(applyWallAvoidanceTru) {
&#9;&#9;&#9;&#9;applyWallAvoidance(posBounds.getBounds());
&#9;&#9;&#9;}
&#9;&#9;}

&#9;&#9;private synchronized void applySeparation(List&lt;Fish&gt; nearbyFish) {
&#9;&#9;&#9;Pos sepDpos = dpos.zeros();
&#9;&#9;&#9;Pos sepNonsimilarDpos = dpos.zeros();
&#9;&#9;&#9;int count = 0, countNonsimilar = 0;

&#9;&#9;&#9;for(Fish o : nearbyFish) {
&#9;&#9;&#9;&#9;if(o != this) {
&#9;&#9;&#9;&#9;&#9;Pos posDiff = getPosDiff(o);
&#9;&#9;&#9;&#9;&#9;double dist = posDiff.magnitude(); // Notice: in future, +`Pos::boundHypotenus(o)`
&#9;&#9;&#9;&#9;&#9;if(isSimilarTo(o)) {
&#9;&#9;&#9;&#9;&#9;&#9;if(forcesSeparation.posIfDistScaleSum(sepDpos, posDiff, dist)) {
&#9;&#9;&#9;&#9;&#9;&#9;&#9;count++;
&#9;&#9;&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;&#9;&#9;} else {
&#9;&#9;&#9;&#9;&#9;&#9;if(forcesSeparationNonsimilar.posIfDistScaleSum(sepNonsimilarDpos, posDiff, dist)) {
&#9;&#9;&#9;&#9;&#9;&#9;&#9;countNonsimilar++;
&#9;&#9;&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;}

&#9;&#9;&#9;if(count &gt; 0) {
&#9;&#9;&#9;&#9;sepDpos.slashEqualsScalar(count);
&#9;&#9;&#9;&#9;forcesSeparation.dposScaleSum(dpos, d2Pos, sepDpos);
&#9;&#9;&#9;}
&#9;&#9;&#9;if(countNonsimilar &gt; 0) {
&#9;&#9;&#9;&#9;sepNonsimilarDpos.slashEqualsScalar(countNonsimilar);
&#9;&#9;&#9;&#9;forcesSeparationNonsimilar.dposScaleSum(dpos, d2Pos, sepNonsimilarDpos);
&#9;&#9;&#9;}
&#9;&#9;}

&#9;&#9;private synchronized void applyAlignment(List&lt;Fish&gt; nearbyFish) {
&#9;&#9;&#9;Pos avgDpos = dpos.zeros();
&#9;&#9;&#9;int count = 0;

&#9;&#9;&#9;for(Fish o : nearbyFish) {
&#9;&#9;&#9;&#9;if(o != this &amp;&amp; isSimilarTo(o)) {
&#9;&#9;&#9;&#9;&#9;Pos posDiff = getPosDiff(o);
&#9;&#9;&#9;&#9;&#9;double distPow2 = posDiff.magnitudePow2();
&#9;&#9;&#9;&#9;&#9;if(forcesAlignment.posIfDistPow2Sum(avgDpos, o.dpos, distPow2)) {
&#9;&#9;&#9;&#9;&#9;&#9;count++;
&#9;&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;}

&#9;&#9;&#9;if(count &gt; 0) {
&#9;&#9;&#9;&#9;avgDpos.slashEqualsScalar(count);
&#9;&#9;&#9;&#9;forcesAlignment.dposScaleSum(dpos, d2Pos, avgDpos);
&#9;&#9;&#9;}
&#9;&#9;}

&#9;&#9;private synchronized void applyCohesion(List&lt;Fish&gt; nearbyFish) {
&#9;&#9;&#9;Pos avgPos = pos.zeros();
&#9;&#9;&#9;int count = 0;

&#9;&#9;&#9;for(Fish o : nearbyFish) {
&#9;&#9;&#9;&#9;if(o != this &amp;&amp; isSimilarTo(o)) {
&#9;&#9;&#9;&#9;&#9;Pos posDiff = getPosDiff(o);
&#9;&#9;&#9;&#9;&#9;double distPow2 = posDiff.magnitudePow2();
&#9;&#9;&#9;&#9;&#9;if(forcesCohesion.posIfDistPow2Sum(avgPos, o.pos, distPow2)) {
&#9;&#9;&#9;&#9;&#9;&#9;count++;
&#9;&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;}

&#9;&#9;&#9;if(count &gt; 0) {
&#9;&#9;&#9;&#9;avgPos.slashEqualsScalar(count);
&#9;&#9;&#9;&#9;forcesCohesion.dposScaleSum(dpos, d2Pos, posBounds.posDiff(avgPos, pos));
&#9;&#9;&#9;}
&#9;&#9;}

&#9;&#9;private synchronized void applyWallAvoidance(double[] res) {
&#9;&#9;&#9;Pos avoidancePos = dpos.zeros();

&#9;&#9;&#9;if(pos.pos[0] &lt; forcesBounds.distance) {
&#9;&#9;&#9;&#9;avoidancePos.pos[0] += (forcesBounds.distance - pos.pos[0]);
&#9;&#9;&#9;} else if(pos.pos[0] &gt; res[0] - forcesBounds.distance) {
&#9;&#9;&#9;&#9;avoidancePos.pos[0] -= (pos.pos[0] - (res[0] - forcesBounds.distance));
&#9;&#9;&#9;}
&#9;&#9;&#9;if(pos.pos[1] &lt; forcesBounds.distance) {
&#9;&#9;&#9;&#9;avoidancePos.pos[1] += (forcesBounds.distance - pos.pos[1]);
&#9;&#9;&#9;} else if(pos.pos[1] &gt; res[1] - forcesBounds.distance) {
&#9;&#9;&#9;&#9;avoidancePos.pos[1] -= (pos.pos[1] - (res[1] - forcesBounds.distance));
&#9;&#9;&#9;}

&#9;&#9;&#9;avoidancePos.starEqualsScalar(d2Pos * forcesBounds.factor / forcesBounds.distance);
&#9;&#9;&#9;dpos.plusEquals(avoidancePos);
&#9;&#9;}

&#9;&#9;public synchronized void update() {
&#9;&#9;&#9;// Limit speed
&#9;&#9;&#9;double speed = dpos.magnitude();
&#9;&#9;&#9;if(speed &gt; dposMax) {
&#9;&#9;&#9;&#9;dpos.slashEqualsScalar(speed);
&#9;&#9;&#9;&#9;dpos.starEqualsScalar(dposMax);
&#9;&#9;&#9;}

&#9;&#9;&#9;// Update position
&#9;&#9;&#9;setPos(pos.plus(dpos));
&#9;&#9;}

&#9;&#9;/**
&#9;&#9; * Renders this fish using GLES2 via {@link SdlGles2#drawFilledPolygon}.
&#9;&#9; * Replicates the JavaFX {@code gc.save/translate/rotate/setFill/beginPath/moveTo/lineTo/closePath/fill/restore} sequence.
&#9;&#9; * Fish shape vertices (local space): {@code {0,-10}, {-5,10}, {-2,0}, {2,0}, {5,10}}.
&#9;&#9; * Triangulated (triangle fan from vertex 0): triangles {@code {0,1,2}, {0,2,3}, {0,3,4}}.
&#9;&#9; */
&#9;&#9;public synchronized void render() {
&#9;&#9;&#9;/* Replicate `gc.translate(tx,ty); gc.rotate(angleDeg+90)` as a 2-D rotation matrix. */
&#9;&#9;&#9;double angle = Math.atan2(dpos.pos[1], dpos.pos[0]) + Math.PI / 2.0; /* equiv. to Math.toRadians(Math.toDegrees(atan2) + 90) */
&#9;&#9;&#9;double cosA = Math.cos(angle);
&#9;&#9;&#9;double sinA = Math.sin(angle);
&#9;&#9;&#9;double tx = pos.pos[0];
&#9;&#9;&#9;double ty = pos.pos[1];
&#9;&#9;&#9;/* Fish shape local vertices: same coordinates as original JavaFX path */
&#9;&#9;&#9;final double[][] lv = {{0,-10}, {-5,10}, {-2,0}, {2,0}, {5,10}};
&#9;&#9;&#9;/* Triangulate polygon as fan from vertex 0: {0,1,2}, {0,2,3}, {0,3,4} -&gt; 3 triangles, 9 verts, 18 floats */
&#9;&#9;&#9;final int[][] tris = {{0,1,2}, {0,2,3}, {0,3,4}};
&#9;&#9;&#9;float[] verts = new float[18];
&#9;&#9;&#9;int vi = 0;
&#9;&#9;&#9;for(int[] tri : tris) {
&#9;&#9;&#9;&#9;for(int idx : tri) {
&#9;&#9;&#9;&#9;&#9;double lx = lv[idx][0], ly = lv[idx][1];
&#9;&#9;&#9;&#9;&#9;verts[vi++] = (float)(lx * cosA - ly * sinA + tx); /* x' = x*cos - y*sin + tx */
&#9;&#9;&#9;&#9;&#9;verts[vi++] = (float)(lx * sinA + ly * cosA + ty); /* y' = x*sin + y*cos + ty */
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;}
&#9;&#9;&#9;SdlGles2.drawFilledPolygon(verts, (float)color.getRed(), (float)color.getGreen(), (float)color.getBlue(), 1.0f);
&#9;&#9;}
&#9;};
};</code></pre></div><p></p>]]></content:encoded></item><item><title><![CDATA[If Android asks you to "upgrade", then refuse: all the "upgrade" does is breaks SMS (can not send SMS now)]]></title><description><![CDATA[If you accept the "update", SMS programs will get stuck on "Waiting to connect" forever (no matter how fast your connection is).]]></description><link>https://swudususuwu.substack.com/p/if-android-asks-you-to-upgrade-refuse</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/if-android-asks-you-to-upgrade-refuse</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Mon, 16 Mar 2026 04:06:23 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!RNr2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffe8c6ac-e1e1-4f38-9c83-eec9575996e6_2400x829.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RNr2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffe8c6ac-e1e1-4f38-9c83-eec9575996e6_2400x829.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RNr2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffe8c6ac-e1e1-4f38-9c83-eec9575996e6_2400x829.png 424w, https://substackcdn.com/image/fetch/$s_!RNr2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffe8c6ac-e1e1-4f38-9c83-eec9575996e6_2400x829.png 848w, https://substackcdn.com/image/fetch/$s_!RNr2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffe8c6ac-e1e1-4f38-9c83-eec9575996e6_2400x829.png 1272w, https://substackcdn.com/image/fetch/$s_!RNr2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffe8c6ac-e1e1-4f38-9c83-eec9575996e6_2400x829.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RNr2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffe8c6ac-e1e1-4f38-9c83-eec9575996e6_2400x829.png" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ffe8c6ac-e1e1-4f38-9c83-eec9575996e6_2400x829.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:null,&quot;width&quot;:null,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:97725,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://swudususuwu.substack.com/i/191092848?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffe8c6ac-e1e1-4f38-9c83-eec9575996e6_2400x829.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!RNr2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffe8c6ac-e1e1-4f38-9c83-eec9575996e6_2400x829.png 424w, https://substackcdn.com/image/fetch/$s_!RNr2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffe8c6ac-e1e1-4f38-9c83-eec9575996e6_2400x829.png 848w, https://substackcdn.com/image/fetch/$s_!RNr2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffe8c6ac-e1e1-4f38-9c83-eec9575996e6_2400x829.png 1272w, https://substackcdn.com/image/fetch/$s_!RNr2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fffe8c6ac-e1e1-4f38-9c83-eec9575996e6_2400x829.png 1456w" sizes="100vw" fetchpriority="high"></picture><div></div></div></a></figure></div><div><hr></div><p>If you accept the "update" you also can no longer receive SMS messages (such as the common requirements which bank or government systems have for you to receive SMS codes to login to their services).</p>]]></content:encoded></item><item><title><![CDATA[Since new white headlamps blind incoming traffic, go back to yellow headlamps]]></title><description><![CDATA[ost "Light-Emtting-Diode" headlamps desensitize all onlookers' photoreceptors, unless coated wth yellow phosphors (which just affect red/green photorceptors).]]></description><link>https://swudususuwu.substack.com/p/to-preserve-onlooker-vision-use-yellow-headlamps</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/to-preserve-onlooker-vision-use-yellow-headlamps</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Tue, 10 Mar 2026 03:03:47 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b586c62d-50b5-4223-9d71-21f40be00580_850x462.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>[<strong>Notice</strong>: cover image used from <a href="https://www.researchgate.net/figure/llustrates-the-principle-of-white-light-generation-of-yellow-phosphor-under-excitation-of_fig1_391659764">https://www.researchgate.net/figure/llustrates-the-principle-of-white-light-generation-of-yellow-phosphor-under-excitation-of_fig1_391659764</a> through <a href="https://k12irc.org/topics/copyright-and-fair-use/)_">Fair Use</a>, for illustration of what yellow phosphors do.]</p><p>[<a href="https://swudususuwu.substack.com/p/to-preserve-onlooker-vision-use-yellow-headlamps">This post</a> allows all uses. If you wish to give credit, you can follow <em><a href="https://creativecommons.org/licenses/by/2.0/">Creative Commons Attributions 2</a></em>.]</p><h1>Discussion</h1><p>Humans have red/green/blue photoreceptors: new headlamps appear &#8220;white&#8221; due to saturation of all color channels, versus old &#8220;yellow&#8221; headlamps (<a href="https://www.reddit.com/r/askscience/comments/1kq2el/when_you_cross_2_different_colored_lights_they/">which just emit red+green photons</a>).</p><p>One solution (except to back to incandescent headlamps) is to use new headlamps coated with yellow phosphors, <a href="https://share.google/aimode/V7NESEQB6yfpvxTz3">which this discussion with Google Assistant shows the specifics of</a>. <a href="https://electronics.stackexchange.com/questions/569932/why-are-most-cob-leds-physically-yellow">Most new headlamps include just enough yellow phosphors to produce a mix of photons which appears &#8220;white&#8221; (saturates the visible spectrum)</a>, the solution is just to include more yellow phosphors (once the diode is produced, you can still put a coat of yellow phosphors around the &#8220;bulb&#8221; to conserve onlooker photoreceptors).</p><p>Pure green headlamps are not acceptable on public roads for some jurisdictions, but <a href="https://www.law.cornell.edu/cfr/text/49/571.108">all jurisdictions allow yellow headlamps</a>.</p><h1>Synopsis + resources</h1><div id="youtube2-1mw7nhXpG3U" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;1mw7nhXpG3U&quot;,&quot;startTime&quot;:&quot;80s&quot;,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/1mw7nhXpG3U?start=80s&amp;rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div>]]></content:encoded></item><item><title><![CDATA[How to improve communism]]></title><description><![CDATA[Communism (at its simplest) is small communes which house humans]]></description><link>https://swudususuwu.substack.com/p/how-to-improve-communism</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/how-to-improve-communism</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Sat, 07 Feb 2026 00:02:21 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d8026155-78d5-4c80-bbe9-a739ce4f734a_1934x1080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Communism (at its simplest) is small communes which house humans. Simple communes are similar to <a href="https://www.youtube.com/watch?v=CVBkciLcZVA">communal farms</a>, but which: produce not just food, but all goods, on own. Communism is practical if small groups of humans have sufficient tools to produce all goods (food + clothes + houses), plus sufficient schools to form our youth into normal, functional adult humans.</p><p>One problem with communism is &#8220;<em>The tragedy of the commons</em>&#8220;: arguments over how much each human can harvest (or how much to contribute), which devolves into <a href="https://swudususuwu.substack.com/p/do-some-jurisdictions-still-trust">authoritarianism</a>. One solution involves infinite resources, which is now possible with <a href="https://swudususuwu.substack.com/p/how-to-use-floss-systems-to-produce">autonomous tools (such as Arduino/Elegoo)</a>.</p><p><a href="https://www.youtube.com/watch?v=0OBFxDP7E_c&amp;pp=ygUnSG93IEJsYWNrYm9hcmQgc29mdHdhcmUgYXNzaXN0cyBzY2hvb2xz">Software (such as Blackboard) can now assist professors for schools</a>. Autonomous tools can now mass-produce most goods (autonomous tools can <a href="https://www.youtube.com/shorts/ctAOILhrqf0">sow</a>+<a href="https://www.youtube.com/shorts/FKSqbiY_z2g">harvest crops</a>, plus <a href="https://www.youtube.com/shorts/cMTZlQ1mlzg">harvest mineral ores</a>, plus <a href="https://www.youtube.com/shorts/KK2Q-rQ_xfk">smelt ore into metal</a>, plus <a href="https://www.youtube.com/watch?v=4sva8_m6N7A">cut metal into parts</a>, plus <a href="https://www.youtube.com/watch?v=AASArDcDcFI&amp;pp=ygUmQXV0b25vbW91cyB0b29scyBtaWxsZXIgcHJvZHVjZXMgcGFydHM%3D">weld</a> or <a href="https://www.youtube.com/watch?v=hLDbRm-98cs">assemble parts into new tools</a> or <a href="https://www.youtube.com/watch?v=3L8tskPqdsM">houses</a>). With sufficient autonomous tools, communism improves.</p>]]></content:encoded></item><item><title><![CDATA[[Preview] Formulas (plus data structures) for sims to use for natural skeleton physics]]></title><description><![CDATA[This document uses ./Physics_sims_which_structures_to_use.md#popular-structures-for-sims's definitions of {pixels, textures, vertices, meshes, skeletons, renderers}.

The purpose of this document is to improve thus renderers.]]></description><link>https://swudususuwu.substack.com/p/natural-physics-skeleton-motion-formulas</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/natural-physics-skeleton-motion-formulas</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Fri, 16 Jan 2026 23:02:35 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/62c251d9-40e2-4a45-b22e-56c4fb5c9baa_1807x934.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>[<a href="https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/NaturalPhysicsFormulas.md">This post</a> is <a href="https://creativecommons.org/licenses/by/2.0/">released through </a><em><a href="https://creativecommons.org/licenses/by/2.0/">Creative Commons Attributions 2</a></em><a href="https://creativecommons.org/licenses/by/2.0/"> (which allows all uses)</a>. Version of this post is <strong><a href="https://github.com/SwuduSusuwu/SusuPosts/commit/bed864e53deccd64d0869a13690b16aab5adcbbc">SusuPosts@bed864e</a></strong>. Notice: since <em>SubStack</em> does not show the whole <em><a href="https://markdownlivepreview.org/">LaTeX</a></em> formulas, you should <a href="https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/NaturalPhysicsFormulas.md">view the </a><em><a href="https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/NaturalPhysicsFormulas.md">GitHub</a></em><a href="https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/NaturalPhysicsFormulas.md"> version</a> if you want the precise formulas.]</p><div><hr></div><h1>Definitions used</h1><p>This document uses <a href="https://swudususuwu.substack.com/i/176876856/popular-structures-for-sims">./Physics_sims_which_structures_to_use.md#popular-structures-for-sims</a>'s definitions of {<em>pixels</em>, <em>textures</em>, <em>vertices</em>, <em>meshes</em>, <em>skeletons</em>, <em>renderers</em>}.</p><p>The purpose of this document is to improve thus <em>renderers</em>.</p><h1>Discussion</h1><h2>Question for physics formulas</h2><p>From <a href="https://poe.com/s/alvXHZV5rCyE209iekhn">this discussion</a> with <em><a href="https://poe.com/Solar-Pro-2">Solar-Pro-2</a></em> of how to improve <a href="https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/Physics_sims_which_structures_to_use.md">physics for sim</a>, <strong>Q</strong>:</p><blockquote><p>Numerous sims compute physics for custom skeletons to move around. Such physics compute positions&amp;volumes of limbs (from joint-positions &amp; joint-edges) to prevent self-intersection, plus to compute environment-intersection, plus to compute inverse kinematics to move around. Wish to know which formulas do thus</p></blockquote><h3><em>Solar-Pro-2</em>&#8216;s solution for physics formulas</h3><p>Notice: the <em>block quote</em> which follows was produced from the <em>artificial intelligence</em> known as <em><a href="https://poe.com/Solar-Pro-2">Solar-Pro-2</a></em>.</p><blockquote><p>Computing physics for custom skeletons in simulators (Sims-style or otherwise) involves a combination of <strong>rigid-body dynamics</strong>, <strong>collision detection</strong>, <strong>inverse kinematics (IK)</strong>, and <strong>self-collision avoidance</strong>. Below is a breakdown of the key formulas and methods used in such systems:</p><div><hr></div><h4><strong>1. Forward Kinematics (FK)</strong></h4><p>Determines the position/orientation of each limb (bone) based on joint angles.</p><ul><li><p><strong>Bone Transformation</strong>: Each bone&#8217;s global transform is computed as: T i = T p a r e n t ( i ) &#8901; R i &#8901; T l o c a l ( i ) Where:</p><ul><li><p>T i = global transform of bone i ,</p></li><li><p>R i = rotation matrix (from joint angles),</p></li><li><p>T l o c a l ( i ) = local translation/scale of the bone.</p></li></ul></li><li><p><strong>Rotation Representations</strong>:</p><ul><li><p><strong>Euler Angles</strong>: &#952; x , &#952; y , &#952; z</p></li><li><p><strong>Quaternions</strong>: q = w + x i + y j + z k (preferable for avoiding gimbal lock).</p></li><li><p><strong>Axis-Angle</strong>: v &#8901; &#952;</p></li></ul></li></ul><div><hr></div><h4><strong>2. Inverse Kinematics (IK)</strong></h4><p>Solves for joint angles given a desired end-effector (e.g., hand or foot) position.</p><h5><strong>Analytical IK (for simple chains, e.g., 2D/3D arm)</strong></h5><ul><li><p><strong>2D Example (Revolute Joints)</strong>: &#952; 1 = atan2 ( y t a r g e t , x t a r g e t ) &#8722; atan2 ( L 2 sin &#8289; ( &#952; 2 ) , L 1 + L 2 cos &#8289; ( &#952; 2 ) ) &#952; 2 = &#960; &#8722; acos ( L 1 2 + L 2 2 &#8722; d 2 2 L 1 L 2 ) Where d = distance to target.</p></li><li><p><strong>3D CCD (Cyclic Coordinate Descent)</strong>: An iterative method that aligns each joint toward the target in sequence: &#916; &#952; i = arctan2 ( | v i &#215; d i | , v i &#8901; d i ) Where v i ) = b o n e d i r e c t i o n , ( d i = vector to target.</p></li></ul><h5><strong>Jacobian-Based IK (for complex chains)</strong>:</h5><p>&#952; &#729; = J &#8224; &#8901; v e r r o r Where:</p><ul><li><p>J = Jacobian matrix (relates joint velocities to end-effector velocity),</p></li><li><p>J &#8224; = pseudoinverse of J ,</p></li><li><p>v e r r o r = velocity needed to reach the target.</p></li></ul><div><hr></div><h4><strong>3. Collision Detection &amp; Prevention</strong></h4><h5><strong>Self-Collision Avoidance</strong>:</h5><ul><li><p><strong>Bounding Volumes</strong>:</p><ul><li><p><strong>AABB (Axis-Aligned Bounding Box)</strong>: Overlap = ( max ( x m i n 1 , x m i n 2 ) &#8804; min ( x m a x 1 , x m a x 2 ) ) &#8743; &#8230;</p></li><li><p><strong>Sphere-Sphere</strong>: Distance &#8804; r 1 + r 2</p></li><li><p><strong>Capsule-Capsule</strong> (common for limbs): Distance between line segments &#8804; r 1 + r 2</p></li></ul></li><li><p><strong>Penetration Resolution</strong>: If collision occurs, apply repulsive forces: $$\mathbf{F}<em>{rep} = k_p \cdot \mathbf{n} \cdot \delta + k_d \cdot \mathbf{v}</em>{rel} \cdot \mathbf{n}$$ Where:</p><ul><li><p>&#948; = penetration depth,</p></li><li><p>n = normal at contact point,</p></li><li><p>k p , k d = proportional/derivative gains.</p></li></ul></li></ul><h5><strong>Environment Collision</strong>:</h5><ul><li><p><strong>Raycasting/Proximity Queries</strong>: For foot placement or hand interactions, check distance to ground/obstacles.</p></li></ul><div><hr></div><h4><strong>4. Joint Limits &amp; Dynamics</strong></h4><ul><li><p><strong>Joint Torque Constraints</strong>: &#964; = k p ( &#952; d e s i r e d &#8722; &#952; c u r r e n t ) &#8722; k d &#969; c u r r e n t Where &#964; ) = a p p l i e d t o r q u e , ( &#969; = angular velocity.</p></li><li><p><strong>Collision-Driven Joint Adjustments</strong>: If a limb intersects another, adjust joint angles</p></li></ul></blockquote><div><hr></div><h2>Question for popular sims</h2><p><strong>Notice</strong>: <em>World of Warcraft</em> includes <a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC12741966/">violent content which can &#8220;reprogram&#8221; your mirror neurons to make you violent</a>. One solution is to limit use to fantastical &#8220;no-contact caster&#8221; classes (such as &#8220;Balance druids&#8221;) or to <a href="https://github.com/SwuduSusuwu/SusuJava/blob/preview/posts/MultiuserConcernsPlusGoals.md#discussion">support roles</a>.</p><ul><li><p><em>World of Warcraft</em> is just used an example of a renderer engine which includes <a href="https://swudususuwu.substack.com/i/176876856/popular-structures-for-sims">skeletons + structures</a> whose interactions would improve from more <a href="https://swudususuwu.substack.com/i/184722746/solar-pro-2s-solution-for-physics-formulas">natural physics formulas</a>. Most of the content is extremely negative: do not install.</p></li></ul><ul><li><p>The sole value of <em>World of Warcraft</em> was that old versions included an <a href="https://wowpedia.fandom.com/wiki/Lua_functions">"addon" system</a> which encouraged users to learn how to use <em><a href="https://www.lua.org/">Lua</a></em> to script the avatars motions (similar to <em><a href="https://www.blender.org/support/tutorials/">Blender</a></em> or <em><a href="https://learnmmd.com/downloads/">Miku Miku Dance</a></em>, plus with shared worlds to interact with (which <em>Blender</em>/<em>Miku Miku Dance</em> do not include)), but new versions have "<a href="https://wowpedia.fandom.com/wiki/Secure_Execution_and_Tainting">tainted</a>" the addon system so that such artificial consciousness is no longer possible. Alternatives (which do not have violence, but which can process artificial consciousness):</p><ul><li><p><em><a href="https://github.com/whoahq/whoa">Whoa</a></em><a href="https://github.com/whoahq/whoa"> is a fan-produced version of the WoW executable which does not use Blizzard&#8217;s code</a>. Since <em>Whoa</em> is opensource, <em>Whoa</em> can use <em>Lua</em> scripts to script movements/dances. <em>Whoa</em> does not support <em>Blizzard</em>&#8216;s servers, just fan-servers (such as <em>AzerothCore</em>). Solutions continued: <a href="https://swudususuwu.substack.com/p/natural-physics-skeleton-motion-formulas#%C2%A7world-of-warcraft-userbots">World of Warcraft userbots</a>.</p></li><li><p><em><a href="https://create.roblox.com/docs/luau">Roblox</a></em><a href="https://create.roblox.com/docs/luau"> has &#8220;</a><em><a href="https://create.roblox.com/docs/luau">Luau&#8221;</a></em> which can do what the original <em>WoW</em> could do with <em>Lua</em>, plus more.</p></li><li><p><em><a href="https://creators.vrchat.com/worlds/udon/">VRChat</a></em><a href="https://creators.vrchat.com/worlds/udon/"> has &#8220;</a><em><a href="https://creators.vrchat.com/worlds/udon/">Udon&#8221;</a></em> which can do what the original <em>WoW</em> could do with <em>Lua</em>, plus more.</p></li><li><p><em><a href="https://docs.chilloutvr.net/cck/lua/">ChilloutVR</a></em><a href="https://docs.chilloutvr.net/cck/lua/"> has &#8220;</a><em><a href="https://docs.chilloutvr.net/cck/lua/">Moonsharp&#8221;</a></em> which can do what the original <em>WoW</em> could do with <em>Lua</em>, plus more. <em><a href="https://moonsharp.org/">Moonsharp</a></em><a href="https://moonsharp.org/"> is one of numerous derivatives of </a><em><a href="https://moonsharp.org/">Lua</a></em>.</p></li><li><p><em><a href="https://forum.convai.com/t/introducing-convai-sim-a-simulation-platform-for-ai-powered-virtual-humans/4644">Convai Sim</a></em><a href="https://forum.convai.com/t/introducing-convai-sim-a-simulation-platform-for-ai-powered-virtual-humans/4644">&#8216;s description is &#8220;Simulation platform for AI-Powered Virtual Humans&#8221;</a>: &#8220;Convai&#8217;s no-code simulation platform &#8212; a powerful suite of tools to create, animate, and deploy AI-powered virtual humans and 3D simulations right in the browser.&#8221;</p></li><li><p><em><a href="https://arxiv.org/abs/1712.05474">AI2-THOR</a></em><a href="https://arxiv.org/abs/1712.05474">&#8216;s description is &#8220;An Interactive 3D Environment for Visual AI&#8221;</a>: &#8220;We introduce The House Of inteRactions (THOR), a framework for visual AI research, available at this http URL. AI2-THOR consists of near photo-realistic 3D indoor scenes, where AI agents can navigate in the scenes and interact with objects to perform tasks. AI2-THOR enables research in many different domains including but not limited to deep reinforcement learning, imitation learning, learning by interaction, planning, visual question answering, unsupervised representation learning, object detection and segmentation, and learning models of cognition.&#8221;</p></li><li><p><em><a href="https://wiki.secondlife.com/wiki/Getting_started_with_LSL">Second Life</a></em><a href="https://wiki.secondlife.com/wiki/Getting_started_with_LSL"> has &#8220;</a><em><a href="https://wiki.secondlife.com/wiki/Getting_started_with_LSL">Linden Script Language&#8221;</a></em> which can do what the original <em>WoW</em> could do with <em>Lua</em>, plus more. Uses <em><a href="https://www.firestormviewer.org">Firestorm Viewer</a></em>, which can import assets from <em>Blender</em>.</p><ul><li><p><em><a href="https://opensimulator.dev/wiki/Scripting_Documentation">OpenSimulator</a></em><a href="https://opensimulator.dev/wiki/Scripting_Documentation"> (the opensource version of </a><em><a href="https://opensimulator.dev/wiki/Scripting_Documentation">Second Life</a></em><a href="https://opensimulator.dev/wiki/Scripting_Documentation">) also uses &#8220;</a><em><a href="https://opensimulator.dev/wiki/Scripting_Documentation">Linden Script Language&#8221;</a></em>, which has you host your own &#8220;grid&#8221; of map servers.</p></li><li><p><em><a href="https://www.alifevirtual.com/blog/opensim-lsl-compatibility.php">Alife Virtual</a></em><a href="https://www.alifevirtual.com/blog/opensim-lsl-compatibility.php"> is a hosted grid (instance) of </a><em><a href="https://www.alifevirtual.com/blog/opensim-lsl-compatibility.php">OpenSimulator</a></em><a href="https://www.alifevirtual.com/blog/opensim-lsl-compatibility.php">, which also uses &#8220;</a><em><a href="https://www.alifevirtual.com/blog/opensim-lsl-compatibility.php">Linden Script Language&#8221;</a></em>. Advantages: <a href="https://www.alifevirtual.com/second-life-vs-alife-2025.php">hosts an </a><em><a href="https://www.alifevirtual.com/second-life-vs-alife-2025.php">OpenSim</a></em><a href="https://www.alifevirtual.com/second-life-vs-alife-2025.php"> grid for you, with no cost for most tools</a>.</p></li></ul></li><li><p><em><a href="https://developers.meta.com/horizon-worlds/learn/documentation/typescript/typescript">Meta Horizon Worlds</a></em><a href="https://developers.meta.com/horizon-worlds/learn/documentation/typescript/typescript"> has &#8220;</a><em><a href="https://developers.meta.com/horizon-worlds/learn/documentation/typescript/typescript">TypeScript&#8221;</a></em> which can do what the original <em>WoW</em> could do with <em>Lua</em>, plus more.</p></li><li><p><em><a href="https://wiki.resonite.com/ProtoFlux">Resonite</a></em><a href="https://wiki.resonite.com/ProtoFlux"> has &#8220;</a><em><a href="https://wiki.resonite.com/ProtoFlux">ProtoFlux</a></em> which can do <strong>most of</strong> what the original <em>WoW</em> could do with <em>Lua</em>, plus more. Is a <strong>visual programming language</strong> (<strong>node-based</strong>, not <strong>functional</strong>), so can not do all which <em>Lua</em> can do, but has more access than the (much restricted) access which <em>WoW</em> gives to <em>Lua</em>.</p></li><li><p><a href="https://poe.com/s/aAjxpcHxJJrLO1oUsHb1">This discussion with </a><em><a href="https://poe.com/s/aAjxpcHxJJrLO1oUsHb1">Solar-Pro-2</a></em><a href="https://poe.com/s/aAjxpcHxJJrLO1oUsHb1"> references numerous altneratives</a>.</p></li></ul></li></ul><blockquote><p>Wish to know the reason that new versions of sims such as World of Warcraft still have problems such as: complex skeletons (such as Dracthyr or dragon mounts) have numerous intersections with the environment (unless all alone). Is as if the &#8220;physics&#8221; is limited to simple lists of limb positions which ignore other skeletons. How long until such sims have more natural physics (such as if you crash your Gryphon into a house, the Gryphon does not &#8220;no-clip&#8221; half of its skeleton through the wall, but instead lands on the wall of the house (or bounces off)? In close coridoors, extended skeletons always &#8220;no-clip&#8221; through the walls, regardless of how you move. How long until most sims will have thus will &#8220;smush flat&#8221; against the walls, or &#8220;fold down&#8221; towards your center mass?</p></blockquote><h3><em>Solar-Pro-2</em>&#8216;s solution for popular sims</h3><p>Notice: the <em>block quote</em> which follows was produced from the <em>artificial intelligence</em> known as <em><a href="https://poe.com/Solar-Pro-2">Solar-Pro-2</a></em>.</p><blockquote><p>The issues you&#8217;re describing&#8212;such as <strong>skeleton intersections with the environment</strong>, <strong>lack of realistic collision response</strong>, and <strong>&#8220;no-clipping&#8221; through walls</strong>&#8212;are deeply rooted in the technical and design trade-offs of modern game engines. Here&#8217;s a breakdown of the reasons why these problems persist and how long it might take for meaningful improvements:</p><div><hr></div><h4><strong>1. Why Current Sims (Including WoW) Struggle with Complex Physics</strong></h4><h5><strong>A. Performance Constraints</strong></h5><ul><li><p><strong>Real-time physics for complex skeletons</strong> (like Dracthyr or dragon mounts) is computationally expensive. Every bone-joint system must be checked for collisions with the environment and other skeletons, which scales poorly with the number of players/objects in a scene.</p><ul><li><p><em>Example</em>: A Dracthyr has dozens of bones; checking each for collision with the world and other players at 60 FPS is expensive.</p></li></ul></li><li><p><strong>MMOs like WoW prioritize network and server performance</strong> over per-pixel physics accuracy. Simulating perfect physics for every skeleton would strain both clients and servers.</p></li></ul><h5><strong>B. Collision Detection Limitations</strong></h5><ul><li><p>Most games use <strong>bounding volumes</strong> (AABBs, capsules, or spheres) for collision checks, not per-vertex physics.</p><ul><li><p><em>Problem</em>: A dragon&#8217;s wing might be represented as a single capsule or mesh, leading to &#8220;no-clipping&#8221; when part of it intersects a wall.</p></li><li><p><em>Solution</em>: Proper per-limbed collision detection would require <strong>per-bone physics</strong>, which is rarely implemented due to cost.</p></li></ul></li></ul><h5><strong>C. Animation vs. Physics Trade-off</strong></h5><ul><li><p>Animations in games are often <strong>pre-authored</strong> (motion-captured or keyframed) and blended with physics later. This means:</p><ul><li><p>The skeleton&#8217;s motion is predefined, and physics only adjusts the root motion (feet sliding, etc.).</p></li><li><p><em>Example</em>: When a dragon crashes into a wall, the animation system has no way to &#8220;fold&#8221; the wings unless explicitly programmed.</p></li></ul></li><li><p><strong>Ragdoll physics</strong> (used for falling players) is a limited form of per-bone physics but isn&#8217;t applied to controllable characters or mounts.</p></li></ul><h5><strong>D. Art and Design Priorities</strong></h5><ul><li><p>Games often <strong>favor visual appeal over physical accuracy</strong>. Artists design skeletons to look good in static poses, not to dynamically interact with the world.</p><ul><li><p><em>Example</em>: Dracthyr&#8217;s wings may be designed for spectacle, not to collide realistically with walls.</p></li></ul></li><li><p><strong>Collision meshes</strong> are often simplified or scaled up to avoid false positives, leading to &#8220;no-clipping.&#8221;</p></li></ul><h5><strong>E. Gameplay Balancing</strong></h5><ul><li><p>Perfect physics could break gameplay. If a dragon mount physically collides with every wall, it might become unstuck or fall through the world in unintended ways.</p><ul><li><p><em>Example</em>: In <em>WoW</em>, mounts are designed to &#8220;float&#8221; slightly to avoid getting stuck in terrain.</p></li></ul></li></ul><div><hr></div><h4><strong>2. When Will This Improve?</strong></h4><h5><strong>Short-Term (1&#8211;3 Years)</strong></h5><ul><li><p><strong>Incremental fixes</strong> are more likely:</p><ul><li><p>Better <strong>per-limb collision detection</strong> for mounts/pets (e.g., wings hitting walls).</p></li><li><p><strong>Procedural animation corrections</strong> (e.g., feet adjusting to slopes).</p></li><li><p>However, full-body physics for all skeletons is unlikely due to performance costs.</p></li></ul></li></ul><h5><strong>Mid-Term (3&#8211;5 Years)</strong></h5><ul><li><p><strong>Next-gen engines</strong> (Unreal Engine 6, Unity 2025+) may introduce:</p><ul><li><p><strong>Per-bone physics solvers</strong> for complex skeletons.</p></li><li><p><strong>AI-driven collision avoidance</strong> (e.g., NPCs folding their wings in tight spaces).</p></li><li><p><strong>Hybrid animation-physics systems</strong> (like NVIDIA&#8217;s <strong>PhysX for Animation</strong>).</p></li></ul></li></ul><h5><strong>Long-Term (5+ Years)</strong></h5><ul><li><p><strong>True physics-based animation</strong> could become mainstream if hardware improves (e.g., GPU-accelerated physics, AI-assisted simulations).</p><ul><li><p><em>Example</em>: <em>Hellblade: Senua&#8217;s Sacrifice</em> used physics for cloth, but applying this to full skeletons is a bigger leap.</p></li></ul></li><li><p><strong>AI and machine learning</strong> could help simulate realistic limb interactions without brute-force physics (e.g., predicting how a dragon would fold its wings).</p></li></ul><div><hr></div><h4><strong>3. What Would Fix This?</strong></h4><p>To achieve the behavior you describe (e.g., a Gryphon smushing against a wall), games would need:</p><ol><li><p><strong>Per-bone collision detection</strong> with <strong>penetration resolution</strong>.</p></li><li><p><strong>Dynamic animation blending</strong> to adjust poses based on physics feedback.</p></li><li><p><strong>High-performance physics solvers</strong> (like Havok or NVIDIA PhysX) optimized for skeletons.</p></li><li><p><strong>Artificial intelligence</strong> to predict and avoid collisions (e.g., pathfinding + physics).</p></li></ol><div><hr></div><h4><strong>Conclusion</strong></h4><p>While <strong>small improvements</strong> (better collision meshes, limb-specific</p></blockquote><div><hr></div><h2>Question for popular sims part 2</h2><p>&#8220;[...]&#8221; shows typo fixes</p><blockquote><p>Understood that collision checks with all rendered skeletons would cause &#8220;computational explosion&#8221; (use too much physics processor unit or central proessor unit resources), but most such sims use &#8220;zones&#8221; to improve network + CPU use; each &#8220;instance&#8221; has numerous small zones (each zone is equal to the view distance, or to double the view distance), such that physics just computes within those zones. But this simple &#8220;physics&#8221; just determines whether you can cast stuff on others, or whether the data structures are sent to you at all or not.</p><p>For natural physics, can you not produce sub-zones as large as the largest skeleton, than have the skeleteon-physics compute collisions within those small sub-zones? If natural skeleton physics is still too expensive, what if you just pre-compute physics to &#8220;slow down&#8221; or &#8220;fold extremities towards center of mass&#8221; when your own bounder-box is too close to another bounder-box? This will not allow to [land] on the [sides of houses], nor to climb over all obstacles, [but] will prevent some collisions</p></blockquote><h3><em>Solar-Pro-2</em>&#8216;s solution for popular sims part 2</h3><p>Notice: the <em>block quote</em> which follows was produced from the <em>artificial intelligence</em> known as <em><a href="https://poe.com/Solar-Pro-2">Solar-Pro-2</a></em>.</p><blockquote><p>Your idea of <strong>sub-zones scaled to skeleton size</strong> and <strong>pre-computed collision responses</strong> is a practical compromise between performance and realism. Here&#8217;s how it could work and its limitations:</p><div><hr></div><h4><strong>1. Sub-Zone Physics for Skeleton Collision</strong></h4><h5><strong>How It Could Work:</strong></h5><ul><li><p><strong>Dynamic Zone Partitioning</strong>:</p><ul><li><p>Divide the world into <strong>adaptive sub-zones</strong> (e.g., cubes or spheres) sized to fit the largest skeleton in the game (e.g., a Dracthyr&#8217;s bounding box).</p></li><li><p>Only perform per-bone collision checks within the same sub-zone to limit computational overhead.</p></li><li><p>Zones update dynamically as skeletons move (e.g., a dragon mount enters a new sub-zone).</p></li></ul></li><li><p><strong>Bone-Level Collision Checks</strong>:</p><ul><li><p>Represent each limb as a <strong>collision primitive</strong> (capsule, box, or convex hull).</p></li><li><p>Check for overlaps only between limbs in the same sub-zone.</p></li><li><p>If a collision is detected, apply <strong>penetration resolution</strong> to push limbs apart.</p></li></ul></li></ul><h5><strong>Advantages</strong>:</h5><ul><li><p>Reduces the number of pairwise checks by limiting them to nearby skeletons.</p></li><li><p>Prevents extreme &#8220;no-clipping&#8221; in tight spaces (e.g., a corridor).</p></li><li><p>Works well for <strong>self-collision avoidance</strong> (e.g., a dragon&#8217;s wings not intersecting its tail).</p></li></ul><h5><strong>Limitations</strong>:</h5><ul><li><p><strong>Still expensive for large crowds</strong>: In a battleground with 100 players, each with 50 bones, this could strain the CPU.</p></li><li><p><strong>No environmental interaction</strong>: Skeletons won&#8217;t realistically collide with buildings or terrain unless the environment is also partitioned into sub-zones with physics.</p></li></ul><div><hr></div><h4><strong>2. Pre-Computed &#8220;Fold Towards Center of Mass&#8221;</strong></h4><h5><strong>How It Could Work:</strong></h5><ul><li><p><strong>Proximity-Driven Animation Blending</strong>:</p><ul><li><p>When a skeleton&#8217;s bounding box overlaps with another object (wall, player, etc.), <strong>blend animations</strong> to pull extremities toward the center of mass.</p></li><li><p>Use <strong>inverse kinematics (IK)</strong> to adjust limb positions while maintaining a natural pose.</p></li><li><p>Example: A dragon&#8217;s wings fold inward if it&#8217;s too close to a wall.</p></li></ul></li><li><p><strong>Penalty Forces</strong>:</p><ul><li><p>Apply artificial forces to joints that push limbs away from collisions.</p></li><li><p>Similar to <strong>ragdoll physics</strong> but constrained to pre-authored poses.</p></li></ul></li></ul><h5><strong>Advantages</strong>:</h5><ul><li><p>Avoids computational explosion by relying on animations rather than full physics.</p></li><li><p>Can prevent <strong>self-intersections</strong> and minor environment collisions.</p></li><li><p>Works well for <strong>crowd scenes</strong> where realism isn&#8217;t the priority.</p></li></ul><h5><strong>Limitations</strong>:</h5><ul><li><p><strong>Not physically accurate</strong>: A dragon won&#8217;t &#8220;land on a wall&#8221; or &#8220;bounce off&#8221;; it&#8217;ll just compress its hitbox.</p></li><li><p><strong>No dynamic interaction</strong>: The skeleton won&#8217;t push against or climb obstacles&#8212;it&#8217;ll just deform.</p></li></ul><div><hr></div><h4><strong>3. Hybrid Approach: Sub-Zones + Animation Blending</strong></h4><p>A more realistic middle ground:</p><ol><li><p><strong>Use sub-zones</strong> to limit collision checks to nearby skeletons/environment.</p></li><li><p><strong>For environment collisions</strong>, use simplified physics (e.g., raycasts from feet/wings to detect surfaces).</p></li><li><p><strong>For skeleton-skeleton collisions</strong>, blend animations to fold extremities when overlaps occur.</p></li><li><p><strong>For critical interactions</strong> (e.g., a mount crashing into a wall), trigger a <strong>pre-rigged animation</strong> (e.g., &#8220;impact squash&#8221;).</p></li></ol><h5><strong>Example in </strong><em><strong>WoW</strong></em>:</h5><ul><li><p>A Dracthyr flying into a wall:</p><ul><li><p>Its wings fold inward (animation blend).</p></li><li><p>Its flight path is adjusted (root motion correction).</p></li><li><p>No per-bone physics, but the illusion of collision is maintained.</p></li></ul></li></ul><div><hr></div><h4><strong>4. When Could This Be Implemented?</strong></h4><ul><li><p><strong>Short-Term (1&#8211;2 Years)</strong>:</p><ul><li><p>Games like <em>WoW</em> could adopt <strong>animation blending for collision avoidance</strong> in tight spaces.</p></li><li><p>Sub-zone physics might be tested in smaller-scale games (e.g., <em>Diablo 4</em> or <em>Final Fantasy XVI</em>).</p></li></ul></li><li><p><strong>Mid-Term (3&#8211;5 Years)</strong>:</p><ul><li><p>Next-gen engines (Unreal 6, Unity 2025+) could introduce <strong>per-bone physics solvers</strong> optimized for skeletons.</p></li><li><p><strong>AI-driven physics</strong> (machine learning to predict limb interactions) might reduce computation.</p></li></ul></li><li><p><strong>Long-Term (5+ Years)</strong>:</p><ul><li><p><strong>Full physics-based animation</strong> becomes feasible with better hardware (e.g., GPU-accelerated physics, quantum computing for collision detection).</p></li></ul></li></ul><div><hr></div><h4><strong>Conclusion</strong></h4><p>Your idea of <strong>sub-zones + animation blending</strong> is a viable near-term solution. It won&#8217;t allow a Gryphon to land on a wall, but it can prevent extreme no-clipping and self</p></blockquote><div><hr></div><h1>Synopsis</h1><ul><li><p><a href="https://swudususuwu.substack.com/p/physics-sims-which-structures-to">./posts/Physics_sims_which_structures_to_use.md</a></p></li></ul><h2><em>World of Warcraft</em> userbots</h2><p>Followup question:</p><blockquote><p>Wish to host a server which allows userbots, then encourage users to use this custom <code>WoW.exe</code> executable for <em>Code Golf</em>. This would not include such heuristics (such as the best formulas to move around crowded zones or to combat bosses), just the functions required for users to learn how to produce such, to encourage users to learn <em>Lua</em>. Perhaps the most simple formulas would get included as defaults (such as movement in straight path to target position, or use of the best 2 to 4 skills against monsters, similar to the official <code>WoW.exe</code>&#8216;s &#8220;single button&#8221; mode) as examples of what you are supposed to do, but those would not be competitive, since the purpose is to encourage users to learn <em>Lua</em>. Another purpose of this is for personal use, to research synthetic consciousness.</p></blockquote><div><hr></div><p>Solution from <em>Google Assistant</em> (<strong>notice</strong>: <em>Google Assistant</em> is an artificial intelligence):</p><blockquote><p>To achieve a &#8220;Code Golf&#8221; environment where users script avatars with unlocked Lua functions on an <a href="https://www.azerothcore.org/eluna/">AzerothCore</a> server, you have two primary paths: modifying the official client or using a specialized server-side module.</p><ol><li><p>The &#8220;Lua Unlocker&#8221; Path (Client-Side)</p></li></ol><p>If you want users to use a version of the official <code>WoW.exe</code> for their scripts, you must provide a <strong>Lua Unlocker</strong>. By default, Blizzard &#8220;protects&#8221; functions like <code>CastSpellByName</code> or <code>JumpOrAscendStart</code> to prevent automation.</p><ul><li><p><strong>How it works:</strong> These tools patch the game&#8217;s memory to allow &#8220;insecure&#8221; code (standard AddOns) to call &#8220;protected&#8221; functions.</p></li><li><p><strong>Existing Tools:</strong></p><ul><li><p><strong><a href="https://github.com/CreativeMind1001/zebra">Zebra</a>:</strong> A specialized unlocker for private servers (Vanilla through WoD) that turns conditional jumps into simple jumps to bypass protection.</p></li><li><p><strong><a href="https://github.com/fail46/World-of-Warcraft-Lua-Unlocker">Fail46 Lua Unlocker</a>:</strong> A DLL-based injector that uses pattern scanning to patch any version of WoW after 4.0.6a.</p></li></ul></li><li><p><strong>Suitability for Code Golf:</strong> This is the most &#8220;authentic&#8221; method. Users write standard AddOns in the <code>Interface/AddOns</code> folder, and with the unlocker active, their scripts can move their character and cast spells just like a developer would on an internal build.</p></li></ul><ol start="2"><li><p>The &#8220;Playerbot-Lua&#8221; Path (Headless/Server-Side)</p></li></ol><p>For a purely educational &#8220;Code Golf&#8221; experience without the overhead of a graphical client, you can use <strong><a href="https://github.com/nate123456/playerbot-lua">playerbot-lua</a></strong>.</p><ul><li><p><strong>The Tool:</strong> It is a dedicated Lua scripting API for AzerothCore that allows developers to automate <a href="https://github.com/mod-playerbots/mod-playerbots">Playerbots</a> at runtime.</p></li><li><p><strong>Workflow:</strong> Instead of a <code>WoW.exe</code>, users use a CLI tool to &#8220;deploy&#8221; their Lua scripts to the server. They then use in-game commands like <code>.bot ai load</code> to execute their logic.</p></li><li><p><strong>Educational Value:</strong> It uses an isolated Lua environment, making it perfect for teaching heuristics without risking server stability.</p></li></ul><ol start="3"><li><p>Creating Your Own (The &#8220;Grok-2&#8221; Approach)</p></li></ol><p>If you choose to generate a custom headless client with an AI like Grok-2, you should focus the prompt on integrating the <strong><a href="https://github.com/ElunaLuaEngine/Eluna">Eluna Lua Engine</a></strong> with a networking library.</p><ul><li><p><strong>Structure:</strong> Ask the AI to bridge the <a href="https://github.com/timelostprototype/wow-client">wow-client (Typescript/C++)</a> networking stack with the Eluna API.</p></li><li><p><strong>Goal:</strong> The client should receive world data (nearby_monsters, chat), populate local tables, and expose them to a Lua state that runs files from a local directory.</p></li></ul><p><strong>Recommendation:</strong> For your specific goal of encouraging children/users to learn Lua via &#8220;Code Golf,&#8221; <strong>playerbot-lua</strong> combined with an AzerothCore server is the most mature and &#8220;ready-to-host&#8221; solution available today.</p></blockquote>]]></content:encoded></item><item><title><![CDATA[[Preview] Simple fish sim for Java, which will include reusable public classs (for new sims to use)]]></title><description><![CDATA[./susuwu/Calculus.java: public class Calculus { /* `class Calculus` houses simple trigonometric (transcendental) `public static` functions. */
    ./susuwu/ImmutablePos.java: public class ImmutablePos implements java.lang.Cloneable, java.util.RandomAccess { /* `ImmutablePos` stores constant vectors (first-order tensors). Usage: `double acceptsConsts(ImmutablePos pos)`. */
        ./susuwu/Pos.java
        ./susuwu/ImmutablePos2.java
        ./susuwu/Pos2.java
    ./susuwu/Forces.java: public class Forces implements java.lang.Cloneable "/* Usage: import susuwu.Forces; ... replaces double fooDistance; double fooFactor; with Forces fooForces;, so other doubles are not confused with those. */"
    ./susuwu/ImmutablePosBounds.java: public class ImmutablePosBounds implements java.lang.Cloneable * Usage: ImmutablePosBounds posBounds(PosBoundsMode);. */"
        enum ImmutablePosBounds: stores how sims enforce bounds.
        ./susuwu/PosBounds.java
    ./susuwu/SimUsages.java: class SimUsages shows FpsTextMode statistics such as fps or ms.
    ./susuwu/FishSim.java]]></description><link>https://swudususuwu.substack.com/p/preview-simple-fish-sim-for-java</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/preview-simple-fish-sim-for-java</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Wed, 07 Jan 2026 22:38:50 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5da3da3d-8458-4f01-a90c-c2a15919f58e_2228x1266.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>[Version of this post is <a href="https://github.com/SwuduSusuwu/SusuJava/commit/0854137f91749a273d7068fbb479ccc5d4bcd4fc">SusuJava@0854137</a>. The <em>GitHub</em> <code>pos2_PosBounds</code> branch <a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/posts/FishSim.md">has the most new version</a>.]</p><div><hr></div><h1>Discussion</h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ovsW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e4e758-fc80-4fdf-ab0d-0b8b560299a4_2228x1266.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ovsW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e4e758-fc80-4fdf-ab0d-0b8b560299a4_2228x1266.png 424w, https://substackcdn.com/image/fetch/$s_!ovsW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e4e758-fc80-4fdf-ab0d-0b8b560299a4_2228x1266.png 848w, https://substackcdn.com/image/fetch/$s_!ovsW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e4e758-fc80-4fdf-ab0d-0b8b560299a4_2228x1266.png 1272w, https://substackcdn.com/image/fetch/$s_!ovsW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e4e758-fc80-4fdf-ab0d-0b8b560299a4_2228x1266.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ovsW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e4e758-fc80-4fdf-ab0d-0b8b560299a4_2228x1266.png" width="1456" height="827" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e8e4e758-fc80-4fdf-ab0d-0b8b560299a4_2228x1266.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:827,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:91494,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://swudususuwu.substack.com/i/179378026?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e4e758-fc80-4fdf-ab0d-0b8b560299a4_2228x1266.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ovsW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e4e758-fc80-4fdf-ab0d-0b8b560299a4_2228x1266.png 424w, https://substackcdn.com/image/fetch/$s_!ovsW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e4e758-fc80-4fdf-ab0d-0b8b560299a4_2228x1266.png 848w, https://substackcdn.com/image/fetch/$s_!ovsW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e4e758-fc80-4fdf-ab0d-0b8b560299a4_2228x1266.png 1272w, https://substackcdn.com/image/fetch/$s_!ovsW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8e4e758-fc80-4fdf-ab0d-0b8b560299a4_2228x1266.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/posts/FishSim.md">./posts/FishSim.md</a> is split from <a href="https://swudususuwu.substack.com/i/147906566/request-java-fish">../SusuPosts/posts/Human_ancestors_are_fish.md#request-java-fish</a>.</p><ul><li><p>The build script moved to <a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/build.sh">./susuwu/build.sh</a>. Usage: <code>./susuwu/build.sh</code></p></li><li><p>The source code moved to <a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/FishSim.java">./susuwu/FishSim.java</a> (<code>package susuwu.FishSim;</code>).</p><ul><li><p>The <a href="https://github.com/SwuduSusuwu/SusuJava/blob/solarPro2FishSim/susuwu/FishSim.java">original version of this source code</a> was <a href="https://poe.com/s/ehlOJYRJNsrGJttfJ4HK">produced through </a><em><a href="https://poe.com/s/ehlOJYRJNsrGJttfJ4HK">Solar-Pro-2</a></em>, but the goal is just to use thus as a template (for future versions to replace all with own source code).</p></li><li><p>Uses <a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/SimUsages.java">./susuwu/SimUsages.java</a>: <code>class SimUsages</code> shows <code>FpsTextMode</code> statistics such as <code>fps</code> or <code>ms</code>. Requirements: some render loop (for measurements). Is not specific to the renderer used. Was produced for <code>class FishSim</code>, so the text (plus comments) assume the organisms are <code>class Fish</code>, but <code>SimUsages</code> is not specific to <code>class Fish</code>.</p></li><li><p>Uses <a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/Calculus.java">./susuwu/Calculus.java</a>: <code>public class Calculus { /* `class Calculus` houses simple trigonometric (transcendental) `public static` functions. Future versions will include true calculus functions (such as "area-under-curve" integrals, or "False Position" or "Quadratic Interpolation" root formulas). */</code></p></li><li><p>Will use <a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/ImmutablePos.java">./susuwu/ImmutablePos.java</a>: <code>public class ImmutablePos implements java.lang.Cloneable, java.util.RandomAccess</code> stores constant vectors (first-order tensors), to future-proof (for volumetrics). <a href="https://github.com/SwuduSusuwu/SusuJava/compare/preview..pos2#diff-8c440bb92bc6939e1450542897e0bbb1a8737b93808ea63ed32784edfacef4b4">Usage: </a><code>double acceptsConsts(ImmutablePos pos)</code>.</p><ul><li><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/Pos.java">./susuwu/Pos.java</a>: <code>public class Pos extends ImmutablePos</code> stores mutable vectors (first-order tensors). Usage: <code>void setsPos(Pos pos)</code>.</p></li><li><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/ImmutablePos2.java">./susuwu/ImmutablePos2.java</a>: <code>public class ImmutablePos2 extends ImmutablePos</code> is the 2-dimensional specialization of <code>class ImmutablePos</code>. Usage: <code>double acceptsConsts(ImmutablePos2 pos2)</code>.</p></li><li><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/Pos2.java">./susuwu/Pos2.java</a>: <code>public class Pos2 extends Pos</code> is the 2-dimensional specialization of <code>class Pos</code>. Usage: <code>Pos2 position;</code>.</p></li></ul></li><li><p>Uses <a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/Forces.java">./susuwu/Forces.java</a>: <code>public class Forces implements java.lang.Cloneable</code> &#8220;/* Usage: <code>import susuwu.Forces;</code> ... replaces <code>double fooDistance; double fooFactor;</code> with <code>Forces fooForces;</code>, so other <code>double</code>s are not confused with those. */&#8221;</p><ul><li><p><code>Forces.posIfDistSum(posDes, posSource, dist)</code>: for <em>Boids</em> groups: <code>if(posIfDistPow2Sum(averagePosOfGroup, posOfIndividual, distPow2ToIndividual) { ++sizeOfGroup; }</code>. For <code>Fish::apply*()</code>, reduces duplicate code.</p></li><li><p><code>Forces.dposScaleSum(dposDes, d2pos, dposSource)</code>: for Boids groups: <code>if(dposScaleSum(derivativeOfPosition, secondDerivOfPos, averageDposOfGroup)) { position += derivativeOfPosition; }</code>, reduces duplicate code.</p></li></ul></li><li><p>Uses <a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/ImmutablePosBounds.java">./susuwu/ImmutablePosBounds.java</a>: <code>public class ImmutablePosBounds implements java.lang.Cloneable</code>, usage: <code>ImmutablePosBounds posBounds(PosBoundsMode);</code></p><ul><li><p><code>enum ImmutablePosBounds</code>: stores how sims enforce bounds.</p><ul><li><p>if <code>PosBoundsMode.wrapAroundResolution</code>, with <code>boundsResolutionFactor = 2</code> the view is close to a natural ocean.</p></li></ul></li><li><p><code>ImmutablePosBounds::getBounds()</code>: to replace <code>FishSim::resolution</code> for physics uses. Introduced <code>bounds</code> for this (to allow out-of-view positions). Notice: for simple sims, this can <code>return resolutionf;</code>.</p><ul><li><p><code>setBounds({resolution[0] * 2, resolution[1] * 2};</code></p></li><li><p><code>boolean isPosInBounds(double[] pos)</code>: replaces duplicate code which tests for if <code>pos</code> is in bounds. Allows 2-dimensions or volumetric.</p></li><li><p><code>boolean posBound(double[] pos, ImmutablePosBounds posBounds)</code>: enforces bounds onto <code>pos</code> (<code>Fish::setPos(newPos)</code> uses this). If <code>PosBoundsMode.boundless</code>, just tests <code>pos</code>.</p></li><li><p><code>public double[] posDiff(double[] pos, double[] o)</code>: reduces duplicate code for complex (such as <code>PosBoundsMode.wrapAroundResolution</code>) distances.</p><ul><li><p><code>Fish::getPosDiff(Fish o)</code>: uses <code>FishSim::posBounds.posDiff</code> so distances follow <code>PosBoundsMode.wrapAroundResolution</code>.</p></li></ul></li><li><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/PosBounds.java">./susuwu/PosBounds.java</a>: <code>public class PosBounds extend ImmutablePosBounds</code> is the mutable (<code>public void set</code>) version of <code>ImmutablePosBounds</code>.</p></li></ul></li></ul></li></ul></li><li><p>The <a href="https://github.com/SwuduSusuwu/SusuJava/blob/solarPro2FishSim/susuwu/FishSim.java">original version of this source code</a> was <a href="https://poe.com/s/ehlOJYRJNsrGJttfJ4HK">produced through </a><em><a href="https://poe.com/s/ehlOJYRJNsrGJttfJ4HK">Solar-Pro-2</a></em>, but the goal is just to use thus as a template (for future versions to replace all with own source code).</p></li></ul><div><hr></div><p>Prefixes (used for variables / functions / classes): <code>+`Class`</code> introduces <code>Class</code>, <code>-`Class`</code> removes <code>Class</code>, <code>@`Class`</code> changes (neutral or improves) <code>Class</code> (as <a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/README.md#git">used for </a><code>git commit</code><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/README.md#git"> messages</a>). <code>:%s/from/to/</code> shows <code>vim</code> regular expressions.</p><p><strong>Notice</strong>: this <code>git branch</code> improves <em><a href="https://github.com/SwuduSusuwu/SusuJava/blob/solarPro2FishSim/susuwu/FishSim.java">Solar-Pro-2</a></em><a href="https://github.com/SwuduSusuwu/SusuJava/blob/solarPro2FishSim/susuwu/FishSim.java">&#8216;s original source code</a> as this list (plus <em><a href="https://github.com/SwuduSusuwu/SusuJava/compare/solarPro2FishSim..susuFishSim#diff-8c440bb92bc6939e1450542897e0bbb1a8737b93808ea63ed32784edfacef4b4">GitHub</a></em><a href="https://github.com/SwuduSusuwu/SusuJava/compare/solarPro2FishSim..susuFishSim#diff-8c440bb92bc6939e1450542897e0bbb1a8737b93808ea63ed32784edfacef4b4">&#8216;s </a><code>/compare/</code><a href="https://github.com/SwuduSusuwu/SusuJava/compare/solarPro2FishSim..susuFishSim#diff-8c440bb92bc6939e1450542897e0bbb1a8737b93808ea63ed32784edfacef4b4"> tool shows</a>) shows:</p><ul><li><p><code>:%s/, 0, 0, 0/, 0, 0/</code>: fixes &#8220;error: method rotate in class Transform cannot be applied to given types; ... actual and formal argument lists differ in length&#8221;</p></li><li><p><a href="https://github.com/SwuduSusuwu/SusuJava/commit/50319ff075fc3a31f761c8fdc1fcce46a2471218">+</a><code>frameCount</code><a href="https://github.com/SwuduSusuwu/SusuJava/commit/50319ff075fc3a31f761c8fdc1fcce46a2471218">, +</a><code>lastTime</code><a href="https://github.com/SwuduSusuwu/SusuJava/commit/50319ff075fc3a31f761c8fdc1fcce46a2471218">, +</a><code>fps</code><a href="https://github.com/SwuduSusuwu/SusuJava/commit/50319ff075fc3a31f761c8fdc1fcce46a2471218">, +</a><code>fpsText</code> to show <strong>FPS</strong> (<a href="https://poe.com/s/OSENRaU2uCb4TznzRPas">produced through </a><em><a href="https://poe.com/s/OSENRaU2uCb4TznzRPas">Solar-Pro-2</a></em>).</p><ul><li><p>@<code>AnimationTimer::handle()</code>: show true <strong>FPS</strong>, plus do not to redraw <code>fpsText</code> unless <code>fps</code> changes.</p></li></ul></li><li><p>@<code>Fish::applySeparation()</code>: reuse values.</p></li><li><p>@<code>Fish::applyWallAvoidance()</code>: reuse values, plus replace <a href="https://stackoverflow.com/questions/43950998/what-are-symbolic-constants-and-magic-constants">magic constants</a> with <code>BOUNDS_DISTANCE</code>.</p></li><li><p>@<code>Fish::update()</code>: if rotation is miniscule, this reuses transforms (to improve <code>fps</code>, but <code>fps</code> is too unstable to notice differences.)</p></li><li><p>+<code>*_FACTOR</code>: (<code>= 1</code> for original results), scales <code>Fish::apply*()</code> forces.</p><ul><li><p>@<code>SEPARATION_FACTOR</code>: (from <code>1</code>) to <code>2</code>, so schools are loose enough to view individual fish.</p></li><li><p>@<code>SEPARATION_DISTANCE</code>: (from <code>50</code>) to <code>22</code>, so fish still school.</p></li></ul></li><li><p>@<code>resolution</code>: (from {800, 600}) to {1280, 720}, since most computers (plus smartphones) can show <em>720p</em> resolution.</p><ul><li><p>@<code>fishCount</code>: (from <code>50</code>) to <code>102</code>, since the window now has more room.</p></li></ul></li><li><p>@<code>Fish::createFishShape()</code>: produce 2 colors of fish.</p><ul><li><p>@<code>class FishSim</code>: is now close to a fluid particle sim which has 2 types of molecules which group to similar molecules (such as <a href="https://thepetrosolutions.com/forums/topic/difference-between-oleophobic-and-oleophilic-impurities/#post-3508">oleophilic compounds</a>) plus separate from nonsimilar molecules (<a href="https://poe.com/s/dYx54tOaDTaDnaBT9TRm">such as oleophobic compounds</a>), except the numerous steps of <em>Boids</em> formula cause some emergent phenomenon which simple molecules do not possess.</p></li></ul></li></ul><div><hr></div><p>Notice: <a href="https://github.com/SwuduSusuwu/SusuJava/commit/6242d2045d619dd664c9a8eea9141c5d178a2ce8">Used </a><em><a href="https://github.com/SwuduSusuwu/SusuJava/commit/6242d2045d619dd664c9a8eea9141c5d178a2ce8">Solar-Pro-2</a></em><a href="https://github.com/SwuduSusuwu/SusuJava/commit/6242d2045d619dd664c9a8eea9141c5d178a2ce8"> to improve codeflow</a> (of the ancestor <code>git commit</code> --- which was half (<code>1 / 2</code>) human-produced source code --- to thus) <a href="https://poe.com/s/ifeHY8AcpVmVC7R5aPB7">so </a><code>fps</code><a href="https://poe.com/s/ifeHY8AcpVmVC7R5aPB7"> improves</a>:</p><ul><li><p>@<code>class FishSim</code>: move <code>class Fish</code>-specific values into @<code>class Fish</code>.</p></li><li><p>@<code>class FishSim</code>: use <code>java.util.concurrent.Executor{s,Service}</code> to offload <code>updateFish()</code> physics (now uses 2 <strong>CPU</strong>s).</p></li><li><p>+<code>gridResolution</code>, @<code>updateFish()</code>: use <code>gridResolution</code> to split <code>List&lt;Fish&gt; fishList</code> into <code>List&lt;Fish&gt;[][] grid</code> (which reduces <em>O(n^2)</em> to <em>O(n^2 / (resolution[] / gridResolution)))</em> <strong>CPU</strong> use)).</p></li><li><p>-<code>javafx.scene.shape.Polygon</code>, +<code>javafx.scene.canvas.Canvas</code>, +<code>javafx.scene.canvas.GraphicsContext</code>: improves renderer <strong>CPU</strong> use?</p></li><li><p>@<code>class FishSim</code>: replaces <code>1.0 / 2 &lt; random.nextDouble()</code> with <code>random.nextBoolean()</code>.</p></li><li><p>{-<code>Fish::createFishShape()</code>, -<code>Fish::getShape()</code>}, {+<code>Fish::render()</code>, +<code>FishSim::renderFish()</code>}: switch to <code>GraphicsContext</code>.</p></li><li><p>@<code>Fish::update</code>: <code>Fish</code> now wrap around (to opposite edges) if out-of-bounds.</p></li><li><p>Notice: the list which follows is all own improvements (versus version above). Own version:</p><ul><li><p>@<code>FishSim::*</code>, @<code>Fish::*</code>: now mutable (since future versions will allow to resize windows plus configure distances). TODO: introduce <code>get*()</code> methods (so that typos do not reconfigure constants, such as view distances).</p></li><li><p>@<code>gridResolution</code>: documents minimum value which enforces <code>*_DISTANCE</code>s.</p></li><li><p>@<code>applyFlockingRules()</code>: replaces magic constants (<code>100</code>) with <code>gridResolution</code> (fixes undefined behaviour if <code>gridResolution</code> changes).</p></li><li><p>@<code>Fish::applyFlockingRules()</code>, @<code>Fish::update()</code>: Replaces magic constants ({<code>2600</code>, <code>1600</code>}) with {<code>resolution[0]</code>, <code>resolution[1]</code>}.</p></li><li><p>@<code>class FishSim</code>: reduces <code>positionInterval</code> (from <code>5</code>) to <code>2</code> (since <code>ExecutorService</code> is used, this does not lower <code>fps</code>) so physics is smooth.</p></li><li><p>+<code>Fish::isSimilarTo()</code>, +<code>isSimilarTolerance</code>: limits schools to similar <code>Fish</code>. @<code>apply*()</code>: uses thus.</p><ul><li><p>+<code>boolean redFishAreAggressiveOrPoisonous</code>: changes how <code>isSimilarTo(Fish other)</code> uses <code>color.getRed()</code></p></li><li><p>@<code>FishSim::start()</code>: produces all possible colors of <code>Fish</code>. <code>Fish</code> schools are now more complex than fluid particles.</p></li></ul></li><li><p>+<code>FishSim::refreshLoop()</code>: houses <code>FishSim::ApplicationTimer::handle()</code>&#8216;s codeflow. Reason: so is simple for future versions to switch <code>new AnimationTimer() {@Override public void handle(long now) { refreshLoop(); }}.start();</code> to alternatives (such as to <code>Timeline timeline = new Timeline(new KeyFrame(Duration.millis(1000.0 / monitorRefreshHertz), event -&gt; { refreshLoop(); })); timeline.setCycleCount(Animation.INDEFINITE); timeline.play();</code>).</p><ul><li><p>@<code>FishSim::fpsText</code>: <code>String.format("%4.2f", fps)</code> (<code>4.</code> so <code>fpsText.size()</code> does not change if <code>fps</code> magnitude does, <code>.2</code> to show miniscule differences).</p></li><li><p>@<code>FishSim::fpsText</code>: show milliseconds used per monitor refresh (&#8221;draw ms&#8221;), plus per <code>updateFish()</code> (&#8221;physics ms&#8221;), plus show <code>fishList.size()</code>, plus <code>fishShown</code>.</p></li></ul></li><li><p>+<code>FishSim::fpsTextRefresh()</code>: houses the <code>FishSim::fpsText</code> codeflow, which <code>refreshLoop()</code> uses.</p><ul><li><p>+<code>FishSim::FpsTextMode()</code>: says which resources for <code>fpsText</code> to show. <code>fpsTextRefresh()</code> uses this.</p></li></ul></li><li><p>@<code>FishSim::*</code>: replaces pairs of 2 <code>int</code>s with <code>int[2]</code> (replaced 2 <code>double</code>s with <code>double[2]</code>), to future-proof (for <code>class Pos2</code>). Such as: -<code>WIDTH</code>, -<code>HEIGHT</code>, +<code>resolution[]</code>.</p><ul><li><p>+<code>double[] resolutionf = {resolution[0], resolution[1]};</code>: for physics code which requires <code>double[]</code>.</p></li><li><p>+<code>FishSim::outOfBounds()</code>: improves @<code>FishSim::updateFish()</code> (which now uses this if <code>Fish</code> not in <code>grid</code> bounds).</p></li><li><p>+<code>void Fish::setPos(double[] newPos)</code>: if <code>Fish</code> not in bounds, uses <code>FishSim::outOfBounds()</code>.</p></li></ul></li><li><p>@<code>FishSim::updateFish()</code>: replaces magic constants (<code>resolution[] / gridResolution</code>) with <code>grid.length</code>, to ensure correct access if the code which produces <code>grid</code> changes.</p><ul><li><p>@<code>FishSim::updateFish()</code>: produces extra <code>grid</code>s if <code>resolution[]</code> is not a multiple of <code>gridResolution</code>, so that <code>Fish</code> with position close to the resolution (close to edges / bounds) are still included.</p></li><li><p>@<code>FishSim::updateFish()</code>: moves bounds test into <code>FishSim::posBounds.posBound()</code>, which <code>Fish::setPos()</code> uses.</p></li><li><p>@<code>class FishSim</code>: +<code>fishVolume</code>, +<code>fishLengthsSep</code>, <code>fishPerVolume</code>: so <code>fishCount</code> scales to resolution.</p></li><li><p>@<code>FishSim::renderFish()</code>: <code>if(posBounds.isPosInBounds(fish.pos))</code> reduces calls to <code>fish.render()</code> (improves <code>fps</code> for sims with huge unshown groups of fish).</p><ul><li><p>@<code>class Fish</code>: +<code>boolean isInBounds;</code> stores <code>boolean posBounds.posBound()</code>&#8216;s <code>return</code> value (improves CPU use). TODO: rename to <code>isVisible</code>?</p><ul><li><p>@<code>FishSim::updateFish()</code>: <code>if(fish.isInBounds) {}</code> around <code>grid[gridPos[0]][gridPos[1]].add(fish);</code>, so <code>FishSim</code> allows out-of-bounds <code>Fish</code>.</p></li></ul></li><li><p>@<code>class Fish</code>: +<code>boolean isVisible</code>: improves <code>FishSim::renderFish()</code> (reduces calls to <code>fish.render()</code>, which improves <code>fps</code> for sims with huge unshown groups of fish).</p></li></ul></li><li><p>+<code>boolean FishSim::setResolution(newResolution)</code>: this sets all variables (plus uses all functions) required for <code>class FishSim</code> to switch to <code>newResolution</code>. Blocks unless has exclusive access to <code>ReentrantLock updateFishLock, renderFishLock;</code>.</p><ul><li><p>+<code>ReentrantLock updateFishLock</code>: @<code>updateFish()</code> blocks unless has exclusive access to this.</p></li><li><p>+<code>ReentrantLock renderFishLock</code>: @<code>renderFish()</code> blocks unless has exclusive access to this.</p></li></ul></li></ul></li></ul></li></ul><p>Notice: replaced most of <em><a href="https://github.com/SwuduSusuwu/SusuJava/blob/solarPro2FishSim/susuwu/FishSim.java">Solar-Pro-2</a></em><a href="https://github.com/SwuduSusuwu/SusuJava/blob/solarPro2FishSim/susuwu/FishSim.java">&#8216;s original </a><code>FishSim.java</code>, as this intro documents (plus <em><a href="https://github.com/SwuduSusuwu/SusuJava/compare/solarPro2FishSim..susuFishSim#diff-8c440bb92bc6939e1450542897e0bbb1a8737b93808ea63ed32784edfacef4b4">GitHub</a></em><a href="https://github.com/SwuduSusuwu/SusuJava/compare/solarPro2FishSim..susuFishSim#diff-8c440bb92bc6939e1450542897e0bbb1a8737b93808ea63ed32784edfacef4b4">&#8216;s </a><code>/compare/</code><a href="https://github.com/SwuduSusuwu/SusuJava/compare/solarPro2FishSim..susuFishSim#diff-8c440bb92bc6939e1450542897e0bbb1a8737b93808ea63ed32784edfacef4b4"> tool shows</a>).</p><div><hr></div><h2>How to improve</h2><ul><li><p>One obvious submodule to introduce is &#8220;predator / prey&#8221; dynamics, but for now have chosen not to introduce thus, <a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/posts/ConcernsOfExistenceMonism.md">due to ethical concerns such as </a><em><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/posts/ConcernsOfExistenceMonism.md">neutral monism</a></em>.</p></li><li><p>Improve <strong>CPU</strong> use (do not know how to, have chose not to use <strong>OOP</strong> <code>class</code>s for most of the physics due to concerns for the <strong>CPU</strong> use). <code>FishSim</code> is physics-bound, guess offload to <strong>GPGPU</strong> can improve this?</p><ul><li><p>Since <code>updateFish()</code>&#8216;s formulas use much CPU, choose how to show smooth motion with <code>24 &gt; physicsFps</code>.</p></li><li><p>@<code>updateFish()</code>: move <code>fish.pos += fish.dpos</code> into <code>refreshLoop()</code>, as ? Or into a separate +<code>physicsLoop()</code>?</p></li><li><p>Replace <code>fish.pos += fish.dpos</code> with <code>fish.pos += fish.dpos * factor</code> to ensure motion is continuous.</p></li><li><p><code>factor = physicsRefreshHertz / fps</code> or <code>factor = physicsRefreshHertz / physicsFps</code>?</p></li></ul></li><li><p>Improve the user interface (now is just <code>fpsTextRefresh()</code><a href="https://github.com/SwuduSusuwu/SusuJava/blob/d21ed39be6640e94f3091e934717e8c7cfbdc758/susuwu/SimUsages.java#L103-L143">, which shows resource use</a>, plus <code>renderFish()</code>&#8216;s viewport which shows the models of <code>class Fish</code>), to allow to scroll the viewport around (requires <code>resolution</code> to include 2 more <code>double</code>s, to store viewport offset, which <code>fish.isVisible</code> must use), plus allow to set numerous options (which for now have &#8220;functional constness&#8221; so those invariants allow <code>java</code> to use more efficient execution), plus allow the user to control one of the organisms (which for now are just <code>Fish</code>).</p><ul><li><p><code>FishSim</code>&#8216;s renderer (<code>renderFish</code>) is separate from <code>FishSim</code><a href="https://github.com/SwuduSusuwu/SusuJava/blob/702ecb81e2d65e845afcfb609eb9fb3ef105c6ec/susuwu/FishSim.java#L307-L321">&#8216;s physics loop (</a><code>updateFish</code><a href="https://github.com/SwuduSusuwu/SusuJava/blob/702ecb81e2d65e845afcfb609eb9fb3ef105c6ec/susuwu/FishSim.java#L307-L321">)</a> (<code>PhysicsMode physicsMode</code> allows to execute on separate <strong>CPU</strong>s through <code>AnimationTimer</code>, <code>Timeline</code>, or <code>ExecutorService</code>), but still shares the process with the physics loop.</p><ul><li><p>Can <a href="https://github.com/SwuduSusuwu/SusuJava/blob/702ecb81e2d65e845afcfb609eb9fb3ef105c6ec/susuwu/FishSim.java#L261-L265">still execute </a><code>updateFish()</code><a href="https://github.com/SwuduSusuwu/SusuJava/blob/702ecb81e2d65e845afcfb609eb9fb3ef105c6ec/susuwu/FishSim.java#L261-L265"> as part of </a><code>renderFish()</code><a href="https://github.com/SwuduSusuwu/SusuJava/blob/702ecb81e2d65e845afcfb609eb9fb3ef105c6ec/susuwu/FishSim.java#L261-L265"> if </a><code>physicsMode = PhysicsMode.synchronous*</code>, but future versions are supposed to drop synchronous modes.</p></li><li><p>Should split <code>updateFish()</code> into a new executable (such as +<code>./susuwu/PhysicsLoop.java</code>) which produces (plus moves) instances of <code>class Fish</code>, with <code>Fish</code> positions sent to +<code>./susuwu/GraphicsShow.java</code> (which shows <code>Fish</code> at those positions), to allow servers for <code>FishSim</code>.</p></li><li><p><code>updateFish()</code> is difficult to do at 24fps (the minimum which shows smooth motion to humans) with thousands of <code>Fish</code>. If <code>FishSim</code>&#8216;s <code>monitorRefreshHertz</code> is &gt; <code>physicsRefreshHertz</code> (in particular, if <code>24 &gt; physicsRefreshHertz</code>): sum <code>pos += dpos</code> on monitor refresh (or some sort of simple motion loop), so movement is smooth.</p></li></ul></li><li><p>Introduce <a href="https://codingtechroom.com/question/create-basic-java-server">servers</a> for shared experiences. But until <code>FishSim</code> has interfaces for user interactions, servers are limited to <strong>RO</strong> <code>./susuwu/PhysicsLoop.java</code> hosts with passive users. Due to variable latencies to / from servers, what those <code>./susuwu/PhysicsLoop.java</code> servers must do:</p><ul><li><p><code>PhysicsMode physicsMode</code> = <code>PhysicsMode.separateFps</code>.</p></li><li><p>Send the derivative of position (<code>dpos</code>), so that (even in the presence of server congestion) each monitor refresh continues to show smooth movement of <code>Fish</code>s.</p></li><li><p>Send the absolute positions (<code>pos</code>), so that position drifts (due to server congestion, or due to insufficient monitor refresh to follow the physics loop) are set back to shared, true position values.</p></li></ul></li></ul></li><li><p>Introduce continuous goals for <code>class Fish</code> (with no predator / prey dynamics, though, goals are limited), whose progress is stored.</p><ul><li><p>Introduce new organisms (for now <code>class FishSim</code> is limited to <code>class Fish</code>). Do not know specifics, but guess more numerous organisms improves the total list of goals for <code>FishSim</code> organisms to pursue.</p></li><li><p><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/posts/MultiuserConcernsPlusGoals.md">./posts/MultiuserConcernsPlusGoals.md</a> discusses numerous goals to pursue.</p></li></ul></li><li><p>Introduce volumetrics (for now the renderer is 2-dimensional, but most of <code>FishSim</code> was produced as &#8220;dimension-agnostic&#8221; (future-proof) source code (on the <code>pos2</code><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2/susuwu/FishSim.java"> branch version of </a><code>FishSim.java</code>), so guess is simple to do (but new to <code>java</code>, so do not know which libs to use). How to introduce pseudo-volumetric visuals with the current (<code>javafx</code>, 2-dimensional) canvas:</p><ul><li><p>If &#8220;depth&#8221; is introduced (if {<code>Fish.pos</code>, <code>Fish.dpos</code>} include distance to <em>viewport</em>), <code>Fish::render()</code> must introduce occlusion (must hide sections of distant <code>Fish</code> which overlap with <code>Fish</code> which are close to <em>viewport</em>).</p></li><li><p>If <code>Fish::render()</code> uses distance to <em>viewport</em> to scale (<em>geometric resize</em>) the <code>Fish</code> images (which for now simple geometric primitives), the result is pseudo-volumetric models.</p></li><li><p>If <code>Fish::render()</code> uses distance to <em>viewport</em> to &#8220;scale&#8221; (<em>geometric translation</em>) the <code>Fish</code> position (<code>Fish.pos</code>) within the <em>frustum</em>, the result is parallax perspective.</p></li><li><p>If <code>class FishSim</code> allows to &#8220;tilt&#8221; (<em>trapezoidal distortion</em>) the <em>viewport perspective</em>, with the other improvements of this list (occlusion, distance scales, parallax perspectives), the result is similar to &#8220;2.5 dimensional&#8221; (such as <em>Starcraft: Brood Wars</em>) visuals.</p></li></ul></li></ul><div><hr></div><h2>Synopsis</h2><ul><li><p><strong>Q</strong>: &#8220;Produce a C++ <code>class fish</code> which does a fish sim&#8221;. <em><a href="https://poe.com/s/fPpFTNwFxeOUjXuKnSu4">Grok-2</a></em><a href="https://poe.com/s/fPpFTNwFxeOUjXuKnSu4">&#8216;s </a><em><a href="https://poe.com/s/fPpFTNwFxeOUjXuKnSu4">C++</a></em><a href="https://poe.com/s/fPpFTNwFxeOUjXuKnSu4"> fish sim</a></p></li><li><p><strong>Q</strong>: &#8220;Use GLFW+Vulkan to produce virtual fish which swim around&#8221;. <em><a href="https://poe.com/Solar-Pro-2">Solar-Pro-2</a></em>&#8216;s <em><a href="https://github.com/glfw/glfw">GLFW</a></em> + <em><a href="https://github.com/KhronosGroup?q=Vulkan">Vulkan</a></em> <a href="https://poe.com/s/Ou5DxBJQKdLeaRKhvxYK">fish sim source code</a></p></li><li><p><strong>Q</strong>: &#8220;Produce <em>OpenGLES2</em> code which moves the sprites of some fish around.&#8221; <em><a href="https://poe.com/Grok-2">Grok-2</a></em>&#8216;s&#8217;s <em><a href="https://www.khronos.org/opengles/">OpenGLES2</a></em> <a href="https://poe.com/s/ZGtQKWGpBLkOzaTr6SkA">fish sim source code</a>.</p><ul><li><p><code>../SusuPosts/</code> published <a href="https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/Human_ancestors_are_fish.md#request-opengles2-fish">this </a><em><a href="https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/Human_ancestors_are_fish.md#request-opengles2-fish">C++</a></em><a href="https://swudususuwu.substack.com/i/147906566/request-opengles-fish"> version</a>.</p><div><hr></div></li></ul></li></ul><h2>Source code</h2><h3><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/build.java">./susuwu/build.sh</a></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;shell&quot;,&quot;nodeId&quot;:&quot;89ef904e-9885-48c6-9c6a-6b462c7fc8e0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-shell">#!/bin/sh
#
# /* This is the new build script for `./susuwu/FishSim.java`. Notice: must execute this from `./susuwu/`. */
# /* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
#  * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; has the newest version of `./susuwu/build.sh` (henceforth "*this source code*").
#  * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
#  * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those. */

PATH_TO_FX="${PATH_TO_FX:-"/usr/share/openjfx/lib/"}" # /* Notice: prefix " "s in path with slashes, such as "\ ". */
PATH_TO_CLASS="FishSim"
PATH_TO_SOURCE="${PATH_TO_CLASS}.java"
JAVA_MODULES="${JAVA_MODULES} --module-path ${PATH_TO_FX} --add-modules javafx.controls,javafx.fxml" # /* Notice: quotes around `${PATH_TO_FX}` give errors, so ensure to escape the path in `PATH_TO_FX=...` */
#JAVA_MODULES="${JAVA_MODULES} --module-path \"${PATH_TO_FX}\" --add-modules javafx.controls,javafx.fxml" # /* Notice: those quotes (around `${PATH_TO_FX}`) give "error: module not found: javafx.{fxml, controls}", so this version is not used. */
JAVA_FLAGS="${JAVA_FLAGS} --enable-native-access=javafx.graphics"
#JAVA_FLAGS="${JAVA_FLAGS} -XX:+HeapDumpOnOutOfMemoryError " # /* Notice: if "Exception java.lang.OutOfMemoryError occurred dispatching signal SIGINT to handler- the VM may need to be forcibly terminated" then uncomment this to use `jhat java_pid*.hprof` */
JAVA_FLAGS="${JAVA_FLAGS} -enableassertions" # /* Notice: remove `-enableassertions` so performance improves */
export JAVA_BUILD_TEST_FLAGS="-verbose"
export JAVA_TEST_FLAGS="-verbose:module"
if command -v sudo &gt;/dev/null; then
&#9;APTITUDE="sudo apt -y install "
else
&#9;APTITUDE="apt -y install " # /* Fixes "The program sudo is not installed." on platforms such as smartphones */
fi
if ! test -d "${PATH_TO_FX}"; then # /* TODO: search for this if default (**Ubuntu**'s) path is not found */
&#9;${APTITUDE} openjfx || ${APTITUDE} libopenjfx-java
&#9;if ! test -d "${PATH_TO_FX}"; then
&#9;&#9;echo "$0: '${PATH_TO_FX}' dir not found. Use \`${APTITUDE} install openjfx\`, then set '\${PATH_TO_FX}' to the actual libs."
&#9;fi
fi
command -v java &gt;/dev/null || ${APTITUDE} openjdk-21-jdk-headless || ${APTITUDE} default-jdk-headless

if [ -n "${GITHUB_ACTIONS}" ]; then
#shellcheck disable=SC2086 # /* Quotes cause "Unrecognized option:" */
&#9;javac ${JAVA_BUILD_TEST_FLAGS} ${JAVA_MODULES} ${PATH_TO_SOURCE} # /* Gives "error: cannot find symbol\n...\n  symbol:   class Force" */
#&#9;java ${JAVA_TEST_FLAGS} ${JAVA_FLAGS} ${JAVA_MODULES} ${PATH_TO_CLASS} # /* Assumes `main()` will `return` (but, `FishSim.java`'s does not) */
#&#9;java ${JAVA_TEST_FLAGS} ${JAVA_FLAGS} --source 16 ${JAVA_MODULES} ${PATH_TO_SOURCE} # /* Assumes `main()` will `return` (but, `FishSim.java`'s does not) */
else
#&#9;javac ${JAVA_MODULES} ${PATH_TO_SOURCE} # /* Gives "error: cannot find symbol\n...\n  symbol:   class Force" */
#&#9;java ${JAVA_FLAGS} ${JAVA_MODULES} ${PATH_TO_CLASS}
#shellcheck disable=SC2086 # /* Quotes cause "Unrecognized option:" */
&#9;java ${JAVA_FLAGS} --source 16 ${JAVA_MODULES} ${PATH_TO_SOURCE} # /* `---source` is workaround for "error: cannot find symbol\n...\n  symbol:   {class Force,variable Utils}" */
fi

exit $? #Status required so [*CodeQL*](https://docs.github.com/en/code-security/code-scanning/introduction-to-code-scanning/about-code-scanning-with-codeql) passes.</code></pre></div><div><hr></div><h3><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/Calculus.java">./susuwu/Calculus.java</a></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;9c9a3d60-5ca7-4ad3-a694-81dedabfcc1d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; stores the newest version of `./susuwu/Calculus.java` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

/* `class Calculus` houses simple trigonometric (transcendental) `public static` functions. Future versions will include true calculus functions (such as "area-under-curve" integrals, or "False Position" or "Quadratic Interpolation" root formulas).
 * Notice: passing `null` (or `{}`) to variadic functions is undefined, future versions could `return Double.NaN;` or `throw new IllegalArgumentException("double... = null");`
 * `class Calculus` does not use generics since [`java` generics do not allow primitives](https://stackoverflow.com/questions/2721546/why-dont-java-generics-support-primitive-types), but `:%s/double/float/` in `vim` will produce the `float` version (`:%s/double/long/` produces the `long` version). [Valhalla is a possible solution for this](https://openjdk.org/jeps/218)
 * Some `assert`s follow, thus document which arguments to use with this (without `-enableassertions`, thus are not enforced).
 * Some "Usage:" comments follow, which document how to use this.
 */
package susuwu; /* Usage: `import susuwu.Calculus;` */
public class Calculus { /* Usage: `Calculus.function(arguments)` */
&#9;private Calculus() {} // Notice: instantiation of `class Calculus` has no uses (no members, plus all functions are `static`).

&#9;/* Simple trigonometric functions (with 1 source) which `return` 1 result value: */
&#9;public static double pow2(double value) { // Usage: for code which otherwise uses `Math.pow(temp, 2)` (or `indirectGet() * indirectGet()`) this improves CPU use. Versus `varX * varX`, `pow2(varX)` is just 1 token to replace to switch to `varW`, thus less bug-prone for future versions.
&#9;&#9;return value * value;
&#9;}
&#9;public static double volume(double... position) { // Usage: is vararg version of "Cartesian volume"
&#9;&#9;double volume_ = 1;
&#9;&#9;for(double posW : position) {
&#9;&#9;&#9;volume_ *= posW;
&#9;&#9;}
&#9;&#9;return volume_;
&#9;}
&#9;public static double average(double... position) { // Usage: is vararg version of "Arithmetic average"
&#9;&#9;return volume(position) / position.length;
&#9;}
&#9;public static double sum(double... position) { // Usage: `return`s sum of `position[dim]`
&#9;&#9;double sum_ = 0;
&#9;&#9;for(double posW : position) {
&#9;&#9;&#9;sum_ += pow2(posW);
&#9;&#9;}
&#9;&#9;return sum_;
&#9;}
&#9;public static double distancePow2(double... position) { // Usage: if absolute values are not used, replaces `if(threshold &lt; hypotenus(position))` (which uses expensive square roots) with `if(pow2(threshold) &lt; distancePow2(position))` (which does not) to improve CPU use.
&#9;&#9;double distancePow2_ = 0;
&#9;&#9;for(double posW : position) {
&#9;&#9;&#9;distancePow2_ += pow2(posW);
&#9;&#9;}
&#9;&#9;return distancePow2_;
&#9;}
&#9;public static double hypotenus(double... position) { // Usage: is vararg version of `java`'s `double Math.hypot(double, double)`;
&#9;&#9;return Math.sqrt(distancePow2(position));
&#9;}
};</code></pre></div><div><hr></div><h3><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/Forces.java">./susuwu/Forces.java</a></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;c24bafc5-36ff-4a17-8a68-ff7848970de0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; stores the newest version of `./susuwu/Forces.java` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

package susuwu; /* Usage: `import susuwu.Forces;` */
/**
 * {@code class Forces} stores values for physics forces (so other {@code double}s are not confused with thus), plus includes a few functions for use with thus.
 * {@code class Forces} does not use generics since [`java` generics do not allow primitives](https://stackoverflow.com/questions/2721546/why-dont-java-generics-support-primitive-types), but {@code :%s/double/float/} in {@code vim} will produce the {@code float} version ({@code :%s/double/long/} produces the {@code long} version). [Valhalla is a possible solution for this](https://openjdk.org/jeps/218)
 * Some {@code assert}s follow, thus document which arguments to use with this (without {@code -enableassertions}, thus are not enforced).
 * Some "Usage:" comments follow, which document how to use this.
 * Usage: replaces {@code double fooDistance; double fooFactor;} with {@code Forces fooForces;}
 */
public class Forces implements java.lang.Cloneable {
&#9;/* Member variables &amp; constructors:
&#9; * Notice: future versions will move `double distance, factor` into `class ImmutableForces`, with `class Forces extends ImmutableForces`. */
&#9;public double distance = 0.0; // Notice: future versions will use `protected double distance`. Usercode should access through `setDistance(double)` or `getDistance()`.
&#9;public double distancePow2 = Calculus.pow2(distance); /* Usage: stores the `pow(distance, 2)` value, for reuses */
&#9;public double factor = 0.0; // Notice: future versions will use `protected double factor`. Usercode should access through `setFactor(double)` or `getFactor()`.
&#9;public Forces(double distance, double factor) {
&#9;&#9;setFactor(factor);
&#9;&#9;setDistance(distance);
&#9;}
&#9;public Forces(Forces o) {
&#9;&#9;set(o);
&#9;}

&#9;/* Public setter functions: */
&#9;public void set(Forces o) {
&#9;&#9;setFactor(o.factor);
&#9;&#9;setDistance(o.distance);
&#9;}
&#9;public void setFactor(double factor) {
&#9;&#9;this.factor = factor;
&#9;}
&#9;public void setDistance(double distance) {
&#9;&#9;this.distance = distance;
&#9;&#9;this.distancePow2 = Calculus.pow2(distance); // Notice: if uncomment `double distancePow2`, then uncomment this
&#9;}

&#9;/* Public getter functions:
&#9; * Notice: future versions will move `get*()` into `class ImmutableForces`, with `class Forces extends ImmutableForces`. */
&#9;public double getFactor() { return this.factor; }
&#9;public double getDistance() { return this.distance; }
&#9;public double getDistancePow2() { /* Usage: `if(this.getDistancePow2() &lt; Calculus.distancePow2(double...))` replaces `if(this.getDistance() &lt; Calculus.hypotenus(double...))` */
&#9;&#9;return this.distancePow2; // Notice: if uncomment `double distancePow2`, then uncomment this
&#9;}

&#9;/* Comparison functions:
&#9; * Notice: future versions will move {`equals(o)`, `hashCode()`} into `class ImmutableForces`, with `class Forces extends ImmutableForces`. */
&#9;@Override
&#9;public boolean equals(Object o) { /* Usage: `if(!equals(o)) { System.err.println("non-similar values"); }` */
&#9;&#9;if(null == o || !(o instanceof Forces)) {
&#9;&#9;&#9;return false;
&#9;&#9;}
&#9;&#9;return equals((Forces)o);
&#9;}
&#9;public boolean equals(Forces o) { /* Usage: is `equals(Object o)` with low CPU use */
&#9;&#9;return o.distance == distance &amp;&amp; o.factor == factor;
&#9;}
&#9;@Override
&#9;public int hashCode() { /* Usage: `if(hashCode() != hashCode(o)) { System.err.println("non-similar values"); }` */
&#9;&#9;double[] doubleView = { distance, factor };
&#9;&#9;return java.util.Arrays.hashCode(doubleView);
&#9;}

&#9;/* `ImmutableForces` functions, which accept `double[] dest, source` or `Pos dest, ImmutablePos source`:
&#9; * For now, this is just functions for Boids groups (future versions will include physics functions, such as {attraction of masses, {repulsion, attraction} of {opposite, similar} charges}).
&#9; */
&#9;// Usage: for Boids groups: `if(posIfDistSum(averageDposOfGroup, dposOfIndividual, distanceToIndividual) { ++sizeOfGroup; }`
&#9;public boolean posIfDistScaleSum(double[] posDes, double[] posSource, double dist) {
&#9;&#9;dist = Math.max(dist, Double.MIN_NORMAL);  // Notice: `MIN_VALUE` (as epsilon) gives rounding errors, so use minimum normal value
&#9;&#9;return posIfDistSum(posDes, new double[] {posSource[0] / dist, posSource[1] / dist},  dist);
&#9;}
&#9;public boolean posIfDistSum(double[] posDes, double[] posSource, double dist) {
&#9;&#9;if(dist &lt; distance) {
&#9;&#9;&#9;posDes[0] += posSource[0];
&#9;&#9;&#9;posDes[1] += posSource[1];
&#9;&#9;&#9;return true;
&#9;&#9;}
&#9;&#9;return false;
&#9;}
&#9;public boolean posIfDistPow2Sum(double[] posDes, double[] posSource, double distPow2) {
&#9;&#9;if(distPow2 &lt; getDistancePow2()) {
&#9;&#9;&#9;posDes[0] += posSource[0];
&#9;&#9;&#9;posDes[1] += posSource[1];
&#9;&#9;&#9;return true;
&#9;&#9;}
&#9;&#9;return false;
&#9;}
&#9;public boolean posIfDistScaleSum(Pos posDes, ImmutablePos posSource, double dist) {
&#9;&#9;dist = Math.max(dist, Double.MIN_NORMAL); // Notice: `MIN_VALUE` (as epsilon) gives rounding errors, so use minimum normal value
&#9;&#9;return posIfDistSum(posDes, posSource.slashScalar(dist),  dist);
&#9;}
&#9;public boolean posIfDistSum(Pos posDes, ImmutablePos posSource, double dist) {
&#9;&#9;if(dist &lt; distance) {
&#9;&#9;&#9;posDes.plusEquals(posSource);
&#9;&#9;&#9;return true;
&#9;&#9;}
&#9;&#9;return false;
&#9;}
&#9;public boolean posIfDistPow2Sum(Pos posDes, ImmutablePos posSource, double distPow2) {
&#9;&#9;if(distPow2 &lt; getDistancePow2()) {
&#9;&#9;&#9;posDes.plusEquals(posSource);
&#9;&#9;&#9;return true;
&#9;&#9;}
&#9;&#9;return false;
&#9;}

&#9;// Usage: for Boids groups: `if(dposScaleSum(derivativeOfPosition, secondDerivOfPos, averageDposOfGroup)) { position += derivativeOfPosition; }`
&#9;public boolean dposScaleSum(double[] dposDes, double d2posDes, double[] dposSource) {
&#9;&#9;double d2posSource = Math.sqrt(Calculus.pow2(dposSource[0]) + Calculus.pow2(dposSource[1]));
&#9;&#9;if(d2posSource &gt; 0) {
&#9;&#9;&#9;dposDes[0] += (dposSource[0] / d2posSource) * d2posDes * factor;
&#9;&#9;&#9;dposDes[1] += (dposSource[1] / d2posSource) * d2posDes * factor;
&#9;&#9;&#9;return true;
&#9;&#9;}
&#9;&#9;return false;
/*
&#9;&#9;// Is faster, if `Math.sqrt(double)` costs more than `double[] dposSourceMsb = { Math.signum(double), Math.signum(double) }`
&#9;&#9;double[] dposSourcePow2 = { Calculus.pow2(dposSource[0]),  Calculus.pow2(dposSource[1]) };
&#9;&#9;double[] dposSourceMsb = { Math.signum(dposSource[0]),  Math.signum(dposSource[1]) };
&#9;&#9;double d2posSourcePow2 = dposSourcePow2[0] + dposSourcePow2[1];
&#9;&#9;if(d2posSourcePow2 &gt; 0) { // TODO: ensure this scales to the original (with `Math.sqrt(double)`) values
&#9;&#9;&#9;dposDes[0] += (dposSourceMsb[0] * dposSourcePow2[0] / d2posSourcePow2) * d2posDes * factor;
&#9;&#9;&#9;dposDes[1] += (dposSourceMsb[1] * dposSourcePow2[1] / d2posSourcePow2) * d2posDes * factor;
&#9;&#9;&#9;return true;
&#9;&#9;}
&#9;&#9;return false;
*/
&#9;}
&#9;public boolean dposScaleSum(Pos dposDes, double d2posDes, ImmutablePos dposSource) {
&#9;&#9;double d2posSource = dposSource.magnitude();
&#9;&#9;if(d2posSource &gt; 0) {
&#9;&#9;&#9;double magnitude = (d2posDes * factor / d2posSource);
&#9;&#9;&#9;dposDes.plusEquals(dposSource.starScalar(magnitude));
&#9;&#9;&#9;return true;
&#9;&#9;}
&#9;&#9;return false;
&#9;}
};</code></pre></div><div><hr></div><h3><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/ImmutablePos.java">./susuwu/ImmutablePos.java</a>:</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;a338044d-71ac-4ac1-8c3f-bd7e4574a938&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; stores the newest version of `./susuwu/ImmutablePos.java` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

package susuwu; // Usage: `import susuwu.ImmutablePos;`
import java.util.Arrays; // `Arrays.toString([])`
/**
 * {@code class ImmutablePos} stores constant vectors (first-order tensors).
 * {@code class ImmutablePos} does not use generics since [{@code java} generics do not allow primitives](https://stackoverflow.com/questions/2721546/why-dont-java-generics-support-primitive-types), but {@code :%s/double/float/} in {@code vim} will produce the {@code float} version ({@code :%s/double/long/} produces the {@code long} version). [Valhalla is a possible solution for this](https://openjdk.org/jeps/218)
 * Some {@code assert}s follow, thus document which arguments to use with this (without {@code -enableassertions}, thus are not enforced).
 * Some "Usage:" comments follow, which document how to use this.
 * Notice: was unaware of &lt;a href="https://docs.oracle.com/en/java/javase/21/docs/api/jdk.incubator.vector/jdk/incubator/vector/DoubleVector.html"&gt;DoubleVector&lt;/a&gt; when produced this, so no {@code implements DoubleVector} for now.
 * @var pos Stores tensor of position coordinates (for internal {@code package susuwu} uses). Other {@code package}s should use {@link #dims()}, {@link #at(int)}, {@link #set(int, double)}. Notice: {@code class ImmutablePos} was almost set to {@code abstract}, due to confusion over which default {@code pos.length} to use: future versions could use {@code pos = {}}, {@code pos = {0}} or {@code pos = {0, 0, 0}}
 * Usage: {@code double acceptsConsts(ImmutablePos pos)}
 */
public class ImmutablePos implements java.lang.Cloneable, java.util.RandomAccess {
&#9;protected double[] pos = {0, 0};

&#9;/* Constructor functions: */
&#9;public ImmutablePos() {}
&#9;public ImmutablePos(double... o) { /* Usage: `double[] o; ImmutablePos pos = ImmutablePos(o);` conversion constructor, or `ImmutablePos pos = ImmutablePos(0, 0);` manual constructor */
&#9;&#9;pos = o.clone();
&#9;}
&#9;public ImmutablePos(ImmutablePos o) { /* Usage: as clone constructor. TODO: clone virtual function addresses. */
&#9;&#9;pos = o.pos.clone();
&#9;}
&#9;@Override
&#9;public Pos clone() { /* Usage: `ImmutablePos pos = o.clone(); assert o.dims() == pos.dims(); for(int index = pos.dims(); index--; ) { assert o.at(index) == pos.at(index); }` */
&#9;&#9;return new Pos(this);
&#9;}
&#9;public Pos zeros() { /* Usage: `ImmutablePos pos = o.zeros(); assert o.dims() == pos.dims(); for(int index = pos.dims(); index--; ) { assert 0 == pos.at(index); }` */
&#9;&#9;return new Pos();
&#9;}
&#9;public Pos ones() { /* Usage: `ImmutablePos pos = o.ones(); assert o.dims() == pos.dims(); for(int index = pos.dims(); index--; ) { assert 1 == pos.at(index); }` */
&#9;&#9;Pos pos = new Pos();
&#9;&#9;pos.fill_(1);
&#9;&#9;return pos;
&#9;}

&#9;/* Immutable `pos` access functions: */
&#9;@Override
&#9;public String toString() { /* Usage: `String serialized = "pos = " + pos.toString() + ";";` */
&#9;&#9;return Arrays.toString(pos);
&#9;}
&#9;public int dims() { /* Usage: `for(int index = ImmutablePos.dims(); index--; ) { sum += ImmutablePos.at(index); }` */
&#9;&#9;return pos.length; // Notice: `pos.length` is the deprecated optimization of `pos.size();`, for internal uses
&#9;}
&#9;public int size() { /* Usage: adapter for `dims()`, so `class ImmutablePos` is compatible with popular `java` `interface`s such as `DoubleArrayView` */
&#9;&#9;return dims();
&#9;}
&#9;public double at(int index) { /* Usage: source which `assert ImmutablePos.at(index) == ImmutablePos.pos[index];` allows. */
&#9;&#9;assert 0 &lt;= index &amp;&amp; pos.length &gt; index;
&#9;&#9;return pos[index]; // Notice: `pos[index]` trusts `java` to enforce `Array` bounds, is for internal use.
&#9;}
&#9;public double get(int index) { /* Usage: adapter for `at()`, so `class ImmutablePos` is compatible with popular `java` `interface`s such as `DoubleArrayView` */
&#9;&#9;return at(index);
&#9;}

&#9;/* Comparison functions: */
&#9;@Override
&#9;public boolean equals(Object o) { /* Usage: `if(!equals(o)) { System.err.println("non-similar values"); }` */
&#9;&#9;if(null == o || !(o instanceof ImmutablePos)) {
&#9;&#9;&#9;return false;
&#9;&#9;}
&#9;&#9;ImmutablePos oPos = (ImmutablePos)o;
&#9;&#9;if(pos.length != oPos.pos.length) {
&#9;&#9;&#9;return false;
&#9;&#9;}
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;if(oPos.pos[dim] != pos[dim]) {
&#9;&#9;&#9;&#9;return false;
&#9;&#9;&#9;}
&#9;&#9;}
&#9;&#9;return true;
&#9;}
&#9;@Override
&#9;public int hashCode() { /* Usage: `if(hashCode() != hashCode(o)) { System.err.println("non-similar values"); }` */
&#9;&#9;return java.util.Arrays.hashCode(pos);
&#9;}

&#9;/* Immutable simple trigo functions: */
&#9;public double volume() { /* Usage: `double arithmeticProduct = ImmutablePos.volume();` */
&#9;&#9;/*
&#9;&#9;// Notice: uncomment this if `double[] pos;` is replaced with `double pos0, pos1, ...;`
&#9;&#9;double volume_ = 1;
&#9;&#9;for(for int dim = 0, end = dims(): end &gt; dim; dim++) {
&#9;&#9;&#9;volume_ *= get(dim); // Notice: `get(dim)` is slower than direct access, but allows discontinuous storage
&#9;&#9;}
&#9;&#9;return volume_;
&#9;&#9;*/
&#9;&#9;return Calculus.volume(pos); // Notice: assumes continuous storage
&#9;}
&#9;public double magnitudePow2() { /* Is tensor version of `Calculus.pow2(double) + Calculus.pow2(double)`. Usage: `double distanceToOriginPow2 = ImmutablePos.magnitudePow2();` */
&#9;&#9;/*
&#9;&#9;// Notice: uncomment this if `double[] pos;` is replaced with `double pos0, pos1, ...;`
&#9;&#9;double distancePow2_ = 0;
&#9;&#9;for(for int dim = 0, end = dims(): end &gt; dim; dim++) {
&#9;&#9;&#9;distancePow2 += Calculus.pow2(get(dim)); // Notice: `get(dim)` is slower than direct access, but allows discontinuous storage
&#9;&#9;}
&#9;&#9;return distancePow2_;
&#9;&#9;*/
&#9;&#9;return Calculus.distancePow2(pos); // Notice: assumes continuous storage
&#9;}
&#9;public double magnitude() { /* Is tensor version of `java`'s `double Math.hypot(double, double)`. Usage: `double distanceToOrigin = ImmutablePos.magnitude();` */
&#9;&#9;return Math.sqrt(magnitudePow2());
&#9;}
&#9;public double distanceToPow2(ImmutablePos o) { /* Usage: if absolute values are not used, replaces `if(threshold &lt; distanceTo(position))` (which uses expensive `Math.sqrt`) with `if(pow2(threshold) &lt; distanceToPow2(position))` (which does not) to improve CPU use. */
&#9;&#9;int end = pos.length;
&#9;&#9;assert end == o.pos.length;
&#9;&#9;double distancePow2 = 0;
&#9;&#9;for(int dim = 0; end &gt; dim; ++dim) {
&#9;&#9;&#9;distancePow2 += Calculus.pow2(pos[dim] - o.pos[dim]);
&#9;&#9;}
&#9;&#9;return distancePow2;
&#9;}
&#9;public double distanceTo(ImmutablePos o) { /* `return`s Euclidean distance. Usage: `double distToO = ImmutablePos.distanceTo(o);` */
&#9;&#9;return Math.sqrt(distanceToPow2(o));
&#9;}

&#9;/* Immutable vector (simple tensor) functions which accept other tensors:
&#9; * Notice: the functions after this row all require `Pos clone()` for the generic versions, thus no reason exists not to `return` the `Pos` version (which allows implicit conversion to `ImmutablePos`) */
&#9;public Pos plus(double... o) { /* Usage: `Pos.plus(o)` is the tensor version of `(Pos + o)` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.plusEquals(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos minus(double... o) { /* Usage: `Pos.minus(o)` is the tensor version of `(Pos - o)` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.minusEquals(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos star(double... o) { /* Usage: `Pos.star(o)` is the tensor version of `(Pos * o)` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.starEquals(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos slash(double... o) { /* Usage: `Pos.slash(o)` is the tensor version of `(Pos / o)` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.slashEquals(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos modulo(double... o) { /* Usage: `Pos.modulo(o)` is the tensor version of `(Pos % o)` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.moduloEquals(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos pow(double... o) { /* Usage: `Pos.pow(o)` is the tensor version of `Math.pow(Pos, o)` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.powEquals(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos plus(ImmutablePos o) { /* Usage: `Pos.plus(o)` is the tensor version of `(Pos + o)` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.plusEquals(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos minus(ImmutablePos o) { /* Usage: `Pos.minus(o)` is the tensor version of `(Pos - o)` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.minusEquals(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos star(ImmutablePos o) { /* Usage: `Pos.star(o)` is the tensor version of `(Pos * o)` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.starEquals(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos slash(ImmutablePos o) { /* Usage: `Pos.slash(o)` is the tensor version of `(Pos / o)` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.slashEquals(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos modulo(ImmutablePos o) { /* Usage: `Pos.modulo(o)` is the tensor version of `(Pos % o)` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.moduloEquals(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos pow(ImmutablePos o) { /* Usage: `Pos.pow(o)` is the tensor version of `Math.pow(Pos, o)` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.powEquals(o);
&#9;&#9;return pos;
&#9;}

&#9;/* Immutable vector (simple tensor) functions which accept scalars:
&#9; * Notice: almost used `double s` (acronym for "scalar"), but all similar functions use `o` (acronym for "other") */
&#9;public Pos plusScalar(double o) { /* Usage: `plusScalar(o)` is the compressed version of `plus(new double[]{o, o})` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.plusEqualsScalar(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos minusScalar(double o) { /* Usage: `minusScalar(o)` is the compressed version of `minus(new double[]{o, o})` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.minusEqualsScalar(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos starScalar(double o) { /* Usage: `starScalar(o)` is the compressed version of `star(new double[]{o, o})` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.starEqualsScalar(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos slashScalar(double o) { /* Usage: `slashScalar(o)` is the compressed version of `slash(new double[]{o, o})` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.slashEqualsScalar(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos moduloScalar(double o) { /* Usage: `moduloScalar(o)` is the compressed version of `modulo(new double[]{o, o})` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.moduloEqualsScalar(o);
&#9;&#9;return pos;
&#9;}
&#9;public Pos powScalar(double o) { /* Usage: `powScalar(o)` is the compressed version of `pow(new double[]{o, o})` */
&#9;&#9;Pos pos = clone();
&#9;&#9;pos.powEqualsScalar(o);
&#9;&#9;return pos;
&#9;}
};</code></pre></div><div><hr></div><h3><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/Pos.java">./susuwu/Pos.java</a></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;b8df9f20-edd4-4a0b-ad69-7da1af86a419&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; stores the newest version of `./susuwu/Pos.java` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

package susuwu; // Usage: `import susuwu.Pos;`
/**
 * {@code class Pos} stores mutable vectors (first-order tensors).
 * {@code class Pos} does not use generics since [{@code java} generics do not allow primitives](https://stackoverflow.com/questions/2721546/why-dont-java-generics-support-primitive-types), but {@code :%s/double/float/} in {@code vim} will produce the {@code float} version ({@code :%s/double/long/} produces the {@code long} version). [Valhalla is a possible solution for this](https://openjdk.org/jeps/218)
 * Some {@code assert}s follow, thus document which arguments to use with this (without {@code -enableassertions}, thus are not enforced).
 * Some "Usage:" comments follow, which document how to use this.
 * Notice: was unaware of &lt;a href="https://docs.oracle.com/en/java/javase/21/docs/api/jdk.incubator.vector/jdk/incubator/vector/DoubleVector.html"&gt;DoubleVector&lt;/a&gt; when produced this, so no {@code implements DoubleVector} for now.
 * @var pos Stores tensor of position coordinates (for internal {@code package susuwu} uses). Other {@code package}s should use {@link #dims()}, {@link #at(int)}, {@link #set(int, double)}
 */
public class Pos extends ImmutablePos { /* Usage: `void setsPos(Pos pos) { pos.set(...); }`. */
&#9;/* Constructor functions: */
&#9;public Pos() {} /* Usage: as default constructor */
&#9;public Pos(double... o) { /* Usage: `double[] o; ImmutablePos pos = Pos(o);` conversion constructor, or `ImmutablePos pos = Pos(0, 0);` manual constructor */
&#9;&#9;pos = o.clone();
&#9;}
&#9;public Pos(ImmutablePos o) { /* Usage: as conversion constructor. TODO: clone virtual function addresses. */
&#9;&#9;pos = o.pos.clone();
&#9;}

&#9;/* `pos` functions: */
&#9;public double[] getPos() { /* Usage: use as `Pos.pos`. Notice: future versions could replace `double[] pos;` with `double pos0, pos1, ...;`, so use `.at(index)` or `.set(index, newValue)`. */
&#9;&#9;return pos; /* Notice: this trusts `java` to enforce `Array` bounds */
&#9;}
&#9;public void set(int index, double newValue) { /* Usage: `Pos.set(index, newValue)`. */
&#9;&#9;assert pos.length &gt; index;
&#9;&#9;pos[index] = newValue; // Notice: `pos[index]` trusts `java` to enforce `Array` bounds, is optimization for internal use.
&#9;}

&#9;/* Vector (tensor) functions which accept other tensors: */
&#9;public void equals_(double[] o) { /* Usage: `Pos.equals_(o)` is the tensor version of `Pos = o` */
&#9;&#9;assert pos.length == o.length;
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] = o[dim]; // Notice: `pos[index] = o` trusts `java` to enforce `Array` bounds, is optimization of `set(index, o)` for internal use.
&#9;&#9;}
&#9;}
&#9;public void plusEquals(double[] o) { /* Usage: `Pos.plusEquals(o)` is the tensor version of `Pos += o` */
&#9;&#9;assert pos.length == o.length;
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] += o[dim]; // Notice: `pos[index] += o` trusts `java` to enforce `Array` bounds, is optimization of `set(index, get(index) + o)` for internal use.
&#9;&#9;}
&#9;}
&#9;public void minusEquals(double[] o) { /* Usage: `Pos.minusEquals(o)` is the tensor version of `Pos -= o` */
&#9;&#9;assert pos.length == o.length;
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] -= o[dim]; // Notice: `pos[index] $op= o` trusts `java` to enforce `Array` bounds, is optimization of `set(index, get(index) $op o)` for internal use.
&#9;&#9;}
&#9;}
&#9;public void starEquals(double[] o) { /* Usage: `Pos.starEquals(o)` is the tensor version of `Pos *= o` */
&#9;&#9;assert pos.length == o.length;
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] *= o[dim];
&#9;&#9;}
&#9;}
&#9;public void slashEquals(double[] o) { /* Usage: `Pos.slashEquals(o)` is the tensor version of `Pos /= o` */
&#9;&#9;assert pos.length == o.length;
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] /= o[dim];
&#9;&#9;}
&#9;}
&#9;public void moduloEquals(double[] o) { /* Usage: `Pos.moduloEquals(o)` is the tensor version of `Pos %= o` */
&#9;&#9;assert pos.length == o.length;
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] %= o[dim];
&#9;&#9;}
&#9;}
&#9;public void powEquals(double[] o) { /* Usage: `Pos.powEquals(o)` is the tensor version of `Pos = Math.pow(Pos, o)` */
&#9;&#9;assert pos.length == o.length;
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] = Math.pow(pos[dim], o[dim]);
&#9;&#9;}
&#9;}

&#9;/* Vector (tensor) functions which accept other tensors: */
&#9;public void equals_(ImmutablePos o) { /* Usage: `Pos.equals_(o)` is the tensor version of `Pos = o` */
&#9;&#9;equals_(o.pos);
&#9;}
&#9;public void plusEquals(ImmutablePos o) { /* Usage: `Pos.plusEquals(o)` is the tensor version of `Pos += o` */
&#9;&#9;plusEquals(o.pos);
&#9;}
&#9;public void minusEquals(ImmutablePos o) { /* Usage: `Pos.minusEquals(o)` is the tensor version of `Pos -= o` */
&#9;&#9;minusEquals(o.pos);
&#9;}
&#9;public void starEquals(ImmutablePos o) { /* Usage: `Pos.starEquals(o)` is the tensor version of `Pos *= o` */
&#9;&#9;starEquals(o.pos);
&#9;}
&#9;public void slashEquals(ImmutablePos o) { /* Usage: `Pos.slashEquals(o)` is the tensor version of `Pos /= o` */
&#9;&#9;slashEquals(o.pos);
&#9;}
&#9;public void moduloEquals(ImmutablePos o) { /* Usage: `Pos.moduloEquals(o)` is the tensor version of `Pos %= o` */
&#9;&#9;moduloEquals(o.pos);
&#9;}
&#9;public void powEquals(ImmutablePos o) { /* Usage: `Pos.powEquals(o)` is the tensor version of `Pos Math.pow(Pos, o)` */
&#9;&#9;powEquals(o.pos);
&#9;}

&#9;/* Vector (tensor) functions which accept scalars:
&#9; * Notice: almost used `double s` (acronym for "scalar"), but all similar functions use `o` (acronym for "otherInstance") */
&#9;public void fill_(double o) { /* Usage: documents that `equalsScalar(o)` is analogous to the `fill_(o)` of popular tensor `class`s */
&#9;&#9;equalsScalar(o);
&#9;}
&#9;public void equalsScalar(double o) { /* Usage: `equalsScalar(o)` broadcasts the scalar `o` to all, similar to `for(long index = dims(); index--; ) { set(index, o); }` */
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] = o; // Notice: `pos[index] = o` trusts `java` to enforce `Array` bounds, is optimization of `set(index, o)` for internal use.
&#9;&#9;}
&#9;}
&#9;public void plusEqualsScalar(double o) { /* Usage: `plusEquals(new double[]{o, o})` compresses down to `plusEqualsScalar(o)` */
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] += o; // Notice: `pos[index] += o` trusts `java` to enforce `Array` bounds, is optimization of `set(index, get(index) + o)` for internal use.
&#9;&#9;}
&#9;}
&#9;public void minusEqualsScalar(double o) { /* Usage: `minusEquals(new double[]{o, o})` compresses down to `minusEqualsScalar(o)` */
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] -= o; // Notice: `pos[index] $op= o` trusts `java` to enforce `Array` bounds, is optimization of `set(index, get(index) $op o)` for internal use.
&#9;&#9;}
&#9;}
&#9;public void starEqualsScalar(double o) { /* Usage: `starEquals(new double[]{o, o})` compresses down to `starEqualsScalar(o)` */
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] *= o;
&#9;&#9;}
&#9;}
&#9;public void slashEqualsScalar(double o) { /* Usage: `slashEquals(new double[]{o, o})` compresses down to `slashEqualsScalar(o)` */
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] /= o;
&#9;&#9;}
&#9;}
&#9;public void moduloEqualsScalar(double o) { /* Usage: `moduloEquals(new double[]{o, o})` compresses down to `moduloEqualsScalar(o)` */
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] %= o;
&#9;&#9;}
&#9;}
&#9;public void powEqualsScalar(double o) { /* Usage: `powEquals(new double[]{o, o})` compresses down to `powEqualsScalar(o)` */
&#9;&#9;for(int dim = 0; dim &lt; pos.length; ++dim) {
&#9;&#9;&#9;pos[dim] = Math.pow(pos[dim], o);
&#9;&#9;}
&#9;}
};</code></pre></div><div><hr></div><h3><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/ImmutablePos2.java">./susuwu/ImmutablePos2.java</a></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;6814bce1-83ac-43f3-a5c2-f1dbc14070a3&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; stores the newest version of `./susuwu/ImmutablePos2.java` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

package susuwu; // Usage: `import susuwu.ImmutablePos2;`
/**
 * {@code class ImmutablePos2} is the 2-dimensional specialization of {@code class ImmutablePos}, which stores constant vectors (first-order tensors).
 * {@code class ImmutablePos2} does not use generics since [{@code java} generics do not allow primitives](https://stackoverflow.com/questions/2721546/why-dont-java-generics-support-primitive-types), but {@code :%s/double/float/} in {@code vim} will produce the {@code float} version ({@code :%s/double/long/} produces the {@code long} version). [Valhalla is a possible solution for this](https://openjdk.org/jeps/218)
 * Some {@code assert}s follow, thus document which arguments to use with this (without {@code -enableassertions}, thus are not enforced).
 * Some "Usage:" comments follow, which document how to use this.
 * Notice: was unaware of &lt;a href="https://docs.oracle.com/en/java/javase/21/docs/api/jdk.incubator.vector/jdk/incubator/vector/DoubleVector.html"&gt;DoubleVector&lt;/a&gt; when produced this, so no {@code implements DoubleVector} for now.
 * Usage: {@code double acceptsConsts(ImmutablePos2 pos) { double coord = pos.at(index); }}
 * @var pos Stores tensor of position coordinates (for internal {@code package susuwu} uses). Other {@code package}s should use: {@link #dims()}, {@link #at(int)}, {@link #set(int, double)}
 */
public class ImmutablePos2 extends ImmutablePos {
&#9;/* Constructor functions: */
&#9;public ImmutablePos2() {} /* Usage: as default constructor */
&#9;public ImmutablePos2(double pos0, double pos1) { /* Usage: as manual constructor */
&#9;&#9;pos[0] = pos0;
&#9;&#9;pos[1] = pos1;
&#9;}
&#9;public ImmutablePos2(double... o) { /* Usage: `double[] o; ImmutablePos2 pos = ImmutablePos2(o);` conversion constructor */
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.length;
&#9;&#9;pos = o.clone();
&#9;}
&#9;public ImmutablePos2(ImmutablePos2 o) { /* Usage: as clone constructor */
&#9;&#9;assert null != o;
&#9;&#9;pos[0] = o.pos[0];
&#9;&#9;pos[1] = o.pos[1];
&#9;}
&#9;public ImmutablePos2(Pos2 o) { /* Usage: as conversion constructor. TODO: clone virtual function addresses. */
&#9;&#9;assert null != o;
&#9;&#9;pos[0] = o.pos[0];
&#9;&#9;pos[1] = o.pos[1];
&#9;}
&#9;public ImmutablePos2(ImmutablePos o) { /* Usage: as conversion constructor. TODO: clone virtual function addresses. */
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.pos.length; // Notice: `2` is optimization of `dims()`, for internal uses
&#9;&#9;pos[0] = o.pos[0];
&#9;&#9;pos[1] = o.pos[1]; // Notice: assumes `o.dims() &gt;= 2`
&#9;}
&#9;public ImmutablePos2(Pos o) { /* Usage: as conversion constructor. TODO: clone virtual function addresses. */ /* Notice: must use `interface` (or multiple inheritance) to allow implicit conversion of `Pos2` into `ImmutablePos` plus into `ImmutablePos2` */
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.pos.length; // Notice: `2` is optimization of `dims()`, for internal uses
&#9;&#9;pos[0] = o.pos[0];
&#9;&#9;pos[1] = o.pos[1]; // Notice: assumes `o.dims() &gt;= 2`
&#9;}
&#9;@Override
&#9;public Pos clone() { /* Usage: `ImmutablePos pos = o.clone(); assert o.dims() == pos.dims(); for(int index = pos.dims(); index--; ) { assert o.at(index) == pos.at(index); }` */
&#9;&#9;return new Pos2(this);
&#9;}
&#9;@Override
&#9;public Pos zeros() { /* Usage: `ImmutablePos pos = o.zeros(); assert o.dims() == pos.dims(); for(int index = pos.dims(); index--; ) { assert 0 == pos.at(index); }` */
&#9;&#9;return new Pos2();
&#9;}
&#9;@Override
&#9;public Pos ones() { /* Usage: `ImmutablePos pos = o.ones(); assert o.dims() == pos.dims(); for(int index = pos.dims(); index--; ) { assert 1 == pos.at(index); }` */
&#9;&#9;return new Pos2(1, 1);
&#9;}

&#9;/* Immutable `pos` access functions */
&#9;@Override
&#9;public int dims() {
&#9;&#9;return 2; // Notice: `2` is optimization of `dims()`, for internal uses
&#9;}
&#9;@Override
&#9;public double at(int index) { /* Usage: source which `assert ImmutablePos.at(index) == ImmutablePos.pos[index];` allows. */
&#9;&#9;assert 0 &lt;= index &amp;&amp; 2 &gt; index; // Notice: `2` is optimization of `dims()`, for internal uses
&#9;&#9;return pos[index]; // Notice: this trusts `java` to enforce `Array` bounds
&#9;}

&#9;/* Comparison functions: */
&#9;@Override
&#9;public boolean equals(Object o) { /* Usage: `if(!equals(o)) { System.err.println("non-similar values"); } */
&#9;&#9;if(null == o || !(o instanceof ImmutablePos)) {
&#9;&#9;&#9;return false;
&#9;&#9;}
&#9;&#9;ImmutablePos oPos = (ImmutablePos)o;
&#9;&#9;return 2 == oPos.pos.length &amp;&amp; // Notice: `2` is optimization of `dims()`, for internal uses
&#9;&#9;&#9;oPos.pos[0] == pos[0] &amp;&amp; oPos.pos[1] == pos[1];
&#9;}
&#9;public boolean equals(ImmutablePos2 o) { /* Usage: `if(!equals(o)) { System.err.println("non-similar values"); } */
&#9;&#9;return null != o &amp;&amp; o.pos[0] == pos[0] &amp;&amp; o.pos[1] == pos[1];
&#9;}
&#9;public boolean equals(Pos2 o) { /* Usage: `if(!equals(o)) { System.err.println("non-similar values"); } */
&#9;&#9;return null != o &amp;&amp; o.pos[0] == pos[0] &amp;&amp; o.pos[1] == pos[1];
&#9;}

&#9;/* Immutable trigo functions: */
&#9;@Override
&#9;public double volume() { /* Usage: `double arithmeticProduct = volume();` */
&#9;&#9;return pos[0] * pos[1];
&#9;}
&#9;@Override
&#9;public double magnitudePow2() { /* Usage: `double euclideanDistanceToOriginPow2 = magnitudePow2();` */
&#9;&#9;double distancePow2 = 0;
&#9;&#9;distancePow2 += Calculus.pow2(pos[0]);
&#9;&#9;distancePow2 += Calculus.pow2(pos[1]);
&#9;&#9;return distancePow2;
&#9;}
&#9;@Override
&#9;public double magnitude() { /* Usage: `double euclideanDistanceToOrigin = magnitude();` */
&#9;&#9;return Math.sqrt(magnitudePow2());
&#9;}
&#9;public double distanceToPow2(ImmutablePos2 o) { /* Usage: if absolute values are not used, replaces `if(threshold &lt; distanceTo(position))` (which uses expensive `Math.sqrt`) with `if(pow2(threshold) &lt; distanceToPow2(position))` (which does not) to improve CPU use. */
&#9;&#9;assert null != o;
&#9;&#9;double distancePow2 = 0;
&#9;&#9;distancePow2 += Calculus.pow2(pos[0] - o.pos[0]);
&#9;&#9;distancePow2 += Calculus.pow2(pos[1] - o.pos[1]);
&#9;&#9;return distancePow2;
&#9;}
&#9;public double distanceTo(ImmutablePos2 o) { /* `return`s Euclidean distance. Usage: `double distToO = ImmutablePos.distanceTo(o);` */
&#9;&#9;return Math.sqrt(distanceToPow2(o));
&#9;}
&#9;@Override
&#9;public double distanceToPow2(ImmutablePos o) { /* Usage: if absolute values are not used, replaces `if(threshold &lt; distanceTo(position))` (which uses expensive `Math.sqrt`) with `if(pow2(threshold) &lt; distanceToPow2(position))` (which does not) to improve CPU use. */
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.pos.length; // Notice: `2` is optimization of `dims()`, for internal uses
&#9;&#9;return distanceToPow2(o);
&#9;}
&#9;@Override
&#9;public double distanceTo(ImmutablePos o) { /* `return`s Euclidean distance. Usage: `double distToO = ImmutablePos.distanceTo(o);` */
&#9;&#9;return Math.sqrt(distanceToPow2(o));
&#9;}
};</code></pre></div><div><hr></div><h3><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/Pos2.java">./susuwu/Pos2.java</a></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;159488fe-f414-4bfb-972b-6344e3dd9146&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; stores the newest version of `./susuwu/Pos2.java` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

package susuwu; // Usage: `import susuwu.Pos2;`
/**
 * {@code class Pos2} is the 2-dimensional specialization of {@code class Pos}, which stores constant vectors (first-order tensors).
 * {@code class Pos2} does not use generics since [{@code java} generics do not allow primitives](https://stackoverflow.com/questions/2721546/why-dont-java-generics-support-primitive-types), but {@code :%s/double/float/} in {@code vim} will produce the {@code float} version ({@code :%s/double/long/} produces the {@code long} version). [Valhalla is a possible solution for this](https://openjdk.org/jeps/218)
 * Some {@code assert}s follow, thus document which arguments to use with this (without {@code -enableassertions}, thus are not enforced).
 * Some "Usage:" comments follow, which document how to use this.
 * Notice: was unaware of &lt;a href="https://docs.oracle.com/en/java/javase/21/docs/api/jdk.incubator.vector/jdk/incubator/vector/DoubleVector.html"&gt;DoubleVector&lt;/a&gt; when produced this, so no {@code implements DoubleVector} for now.
 * Usage: {@code Pos2 position = Pos2(coord[0], coord[1]);}
 * @var pos Stores tensor of position coordinates (for internal {@code package susuwu} uses). Other {@code package}s should use: {@link #dims()}, {@link #at(int)}, {@link #set(int, double)}
 */
public class Pos2 extends Pos {
&#9;/* Constructor functions */
&#9;public Pos2() {} /* Usage: as default constructor */
&#9;public Pos2(double pos0, double pos1) { /* Usage: as manual constructor */
&#9;&#9;pos[0] = pos0;
&#9;&#9;pos[1] = pos1;
&#9;}
&#9;public Pos2(double... o) { /* Usage: `double[] o; Pos2 pos = Pos2(o);` conversion constructor */
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.length; // Notice: `2` is the hardcoded value for `Pos2.dims()`, just for internal use.
&#9;&#9;pos = o.clone();
&#9;}
&#9;public Pos2(Pos2 o) { /* Usage: as clone constructor */
&#9;&#9;assert null != o;
&#9;&#9;pos[0] = o.pos[0];
&#9;&#9;pos[1] = o.pos[1];
&#9;}
&#9;public Pos2(ImmutablePos2 o) { /* Usage: as conversion constructor. */
&#9;&#9;assert null != o;
&#9;&#9;pos[0] = o.pos[0];
&#9;&#9;pos[1] = o.pos[1];
&#9;}
&#9;public Pos2(ImmutablePos o) { /* Usage: as conversion constructor. */
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.pos.length; // Notice: `2` is the hardcoded value for `Pos2.dims()`
&#9;&#9;pos[0] = o.pos[0];
&#9;&#9;pos[1] = o.pos[1];
&#9;}
&#9;public Pos2(Pos o) { /* Usage: as conversion constructor. */
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.pos.length; // Notice: `2` is the hardcoded value for `Pos2.dims()`
&#9;&#9;pos[0] = o.pos[0]; // Notice: is optimization of `set(0, o.at(0));` ..., just for internal use.
&#9;&#9;pos[1] = o.pos[1]; // Notice: assumes `o.dims() &gt;= 2`
&#9;}
&#9;@Override
&#9;public Pos clone() { /* Usage: `ImmutablePos pos = o.clone(); assert o.dims() == pos.dims(); for(int index = pos.dims(); index--; ) { assert o.at(index) == pos.at(index); }` */
&#9;&#9;return new Pos2(pos[0], pos[1]);
&#9;}
&#9;@Override
&#9;public Pos zeros() { /* Usage: `ImmutablePos pos = o.zeros(); assert o.dims() == pos.dims(); for(int index = pos.dims(); index--; ) { assert 0 == pos.at(index); }` */
&#9;&#9;return new Pos2();
&#9;}
&#9;@Override
&#9;public Pos ones() { /* Usage: `ImmutablePos pos = o.ones(); assert o.dims() == pos.dims(); for(int index = pos.dims(); index--; ) { assert 1 == pos.at(index); }` */
&#9;&#9;return new Pos2(1, 1);
&#9;}

&#9;/* `pos` functions: */
&#9;@Override
&#9;public int dims() {
&#9;&#9;return 2; // Notice: is optimization of `return pos.length;`, plus allows future changes of `double[] pos;` to `double pos0, pos1;`
&#9;}
&#9;@Override
&#9;public double at(int index) { /* Usage: source which `assert ImmutablePos.at(index) == ImmutablePos.pos[index];` allows. */
&#9;&#9;assert 0 &lt;= index &amp;&amp; 2 &gt; index; // Notice: `2` is hardcoded value of `Pos2.dims()`, just for internal use.
&#9;&#9;return pos[index]; // Notice: this trusts `java` to enforce `Array` bounds
&#9;}
&#9;@Override
&#9;public void set(int index, double newValue) { /* Usage: `Pos.set(index, newValue)`. */
&#9;&#9;assert 0 &lt;= index &amp;&amp; 2 &gt; index; // Notice: `2` is hardcoded value of `Pos2.dims()`, just for internal use.
&#9;&#9;pos[index] = newValue; // Notice: this trusts `java` to enforce `Array` bounds
&#9;}

&#9;/* Comparison functions: */
&#9;@Override
&#9;public boolean equals(Object o) { /* Usage: `if(!equals(o)) { System.err.println("non-similar values"); }` */
&#9;&#9;if(null == o || !(o instanceof ImmutablePos)) {
&#9;&#9;&#9;return false;
&#9;&#9;}
&#9;&#9;ImmutablePos oPos = (ImmutablePos)o;
&#9;&#9;return 2 == oPos.pos.length &amp;&amp; // Notice: `2 == oPos.pos.length` is optimization of `dims() == oPos.dims()`, for internal use
&#9;&#9;&#9;oPos.pos[0] == pos[0] &amp;&amp; oPos.pos[1] == pos[1]; // Notice: `pos[dim]` is optimization of `at(dim)`, for internal use
&#9;}
&#9;public boolean equals(ImmutablePos2 o) { /* Usage: is `equals(Object o)` with low CPU use*/
&#9;&#9;return null != o &amp;&amp; o.pos[0] == pos[0] &amp;&amp; o.pos[1] == pos[1]; // Notice: `pos[dim]` is optimization of `at(dim)`, for internal use
&#9;}
&#9;public boolean equals(Pos2 o) { /* Usage: is `equals(Object o)` with low CPU use*/
&#9;&#9;return null != o &amp;&amp; o.pos[0] == pos[0] &amp;&amp; o.pos[1] == pos[1];
&#9;}

&#9;/* Immutable trigo functions: */
&#9;@Override
&#9;public double volume() { /* Usage: `double arithmeticProduct = volume();` */
&#9;&#9;return pos[0] * pos[1]; // Notice: `pos[0]` is optimization of `at(0)`, just for internal use.
&#9;}
&#9;@Override
&#9;public double magnitudePow2() { /* Usage: `double euclideanDistanceToOriginPow2 = magnitudePow2();` */
&#9;&#9;double distancePow2 = 0;
&#9;&#9;distancePow2 += Calculus.pow2(pos[0]); // Notice: `pos[0]` is optimization of `at(0)`, just for internal use.
&#9;&#9;distancePow2 += Calculus.pow2(pos[1]); // Notice: `pos[1]` is optimization of `at(1)`, just for internal use.
&#9;&#9;return distancePow2;
&#9;}
&#9;@Override
&#9;public double magnitude() { /* Usage: `double euclideanDistanceToOrigin = magnitude();` */
&#9;&#9;return Math.sqrt(magnitudePow2());
&#9;}
&#9;@Override
&#9;public double distanceToPow2(ImmutablePos o) { /* Usage: if absolute values are not used, replaces `if(threshold &lt; distanceTo(position))` (which uses expensive `Math.sqrt`) with `if(pow2(threshold) &lt; distanceToPow2(position))` (which does not) to improve CPU use. */
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.pos.length; // Notice: is optimization of `assert dims() == o.dims();`, for internal use.
&#9;&#9;double distancePow2 = 0;
&#9;&#9;distancePow2 += Calculus.pow2(pos[0] - o.pos[0]); // Notice: is optimization of `at(0) - o.at(0)`
&#9;&#9;distancePow2 += Calculus.pow2(pos[1] - o.pos[1]); // Notice: is optimization of `at(1) - o.at(1)`
&#9;&#9;return distancePow2;
&#9;}
&#9;@Override
&#9;public double distanceTo(ImmutablePos o) { /* `return`s Euclidean distance. Usage: `double distToO = ImmutablePos.distanceTo(o);` */
&#9;&#9;return Math.sqrt(distanceToPow2(o));
&#9;}

&#9;/* Vector (tensor) functions with `double...`: */
&#9;@Override
&#9;public void plusEquals(double... o) {
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.length; // Notice: `2` is the hardcoded value for `Pos2.dims()`, just for internal use.
&#9;&#9;pos[0] += o[0];
&#9;&#9;pos[1] += o[1];
&#9;}
&#9;@Override
&#9;public void minusEquals(double... o) {
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.length;
&#9;&#9;pos[0] -= o[0];
&#9;&#9;pos[1] -= o[1];
&#9;}
&#9;@Override
&#9;public void starEquals(double... o) {
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.length;
&#9;&#9;pos[0] *= o[0];
&#9;&#9;pos[1] *= o[1];
&#9;}
&#9;@Override
&#9;public void slashEquals(double... o) {
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.length;
&#9;&#9;pos[0] /= o[0];
&#9;&#9;pos[1] /= o[1];
&#9;}
&#9;@Override
&#9;public void moduloEquals(double... o) {
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.length;
&#9;&#9;pos[0] %= o[0];
&#9;&#9;pos[1] %= o[1];
&#9;}
&#9;@Override
&#9;public void powEquals(double... o) {
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.length;
&#9;&#9;pos[0] = Math.pow(pos[0], o[0]);
&#9;&#9;pos[1] = Math.pow(pos[1], o[1]);
&#9;}

&#9;/* Vector (tensor) functions with `ImmutablePos`: */
&#9;@Override
&#9;public void plusEquals(ImmutablePos o) {
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.pos.length; // Notice: `2` is the hardcoded value for `Pos2.dims()`
&#9;&#9;pos[0] += o.pos[0];
&#9;&#9;pos[1] += o.pos[1];
&#9;}
&#9;@Override
&#9;public void minusEquals(ImmutablePos o) {
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.pos.length;
&#9;&#9;pos[0] -= o.pos[0];
&#9;&#9;pos[1] -= o.pos[1];
&#9;}
&#9;@Override
&#9;public void starEquals(ImmutablePos o) {
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.pos.length;
&#9;&#9;pos[0] *= o.pos[0];
&#9;&#9;pos[1] *= o.pos[1];
&#9;}
&#9;@Override
&#9;public void slashEquals(ImmutablePos o) {
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.pos.length;
&#9;&#9;pos[0] /= o.pos[0];
&#9;&#9;pos[1] /= o.pos[1];
&#9;}
&#9;@Override
&#9;public void moduloEquals(ImmutablePos o) {
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.pos.length;
&#9;&#9;pos[0] %= o.pos[0];
&#9;&#9;pos[1] %= o.pos[1];
&#9;}
&#9;@Override
&#9;public void powEquals(ImmutablePos o) {
&#9;&#9;assert null != o;
&#9;&#9;assert 2 == o.pos.length;
&#9;&#9;pos[0] = Math.pow(pos[0], o.pos[0]);
&#9;&#9;pos[1] = Math.pow(pos[1], o.pos[1]);
&#9;}

&#9;/* Vector (tensor) functions with scalars:
&#9; * Notice: almost used `double s` (acronym for "scalar"), but all similar functions use `o` (acronym for "otherInstance") */
&#9;@Override
&#9;public void plusEqualsScalar(double o) { /* Usage: `plusEquals(new double[]{o, o})` compresses down to `plusEqualsScalar(o)` */
&#9;&#9;pos[0] += o;
&#9;&#9;pos[1] += o;
&#9;}
&#9;@Override
&#9;public void minusEqualsScalar(double o) { /* Usage: `minusEquals(new double[]{o, o})` compresses down to `minusEqualsScalar(o)` */
&#9;&#9;pos[0] -= o;
&#9;&#9;pos[1] -= o;
&#9;}
&#9;@Override
&#9;public void starEqualsScalar(double o) { /* Usage: `starEquals(new double[]{o, o})` compresses down to `starEqualsScalar(o)` */
&#9;&#9;pos[0] *= o;
&#9;&#9;pos[1] *= o;
&#9;}
&#9;@Override
&#9;public void slashEqualsScalar(double o) { /* Usage: `slashEquals(new double[]{o, o})` compresses down to `slashEqualsScalar(o)` */
&#9;&#9;pos[0] /= o;
&#9;&#9;pos[1] /= o;
&#9;}
&#9;@Override
&#9;public void moduloEqualsScalar(double o) { /* Usage: `moduloEquals(new double[]{o, o})` compresses down to `moduloEqualsScalar(o)` */
&#9;&#9;pos[0] %= o;
&#9;&#9;pos[1] %= o;
&#9;}
&#9;@Override
&#9;public void powEqualsScalar(double o) { /* Usage: `powEquals(new double[]{o, o})` compresses down to `powEqualsScalar(o)` */
&#9;&#9;pos[0] = Math.pow(pos[0], o);
&#9;&#9;pos[1] = Math.pow(pos[1], o);
&#9;}
};</code></pre></div><div><hr></div><h3><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/ImmutablePosBounds.java">./susuwu/ImmutablePosBounds.java</a></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;de2de8fd-1777-40ca-b0a8-c9b0343d4634&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; stores the newest version of `./susuwu/ImmutablePosBounds.java` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

package susuwu; /* Usage: `import susuwu.ImmutablePosBounds;` */
import java.util.Arrays;
/**
 * `class ImmutablePosBounds` says how sims enforce position bounds.
 * Usage: `ImmutablePosBounds posBounds;`
 * `class ImmutablePosBounds` does not use generics since [`java` generics do not allow primitives](https://stackoverflow.com/questions/2721546/why-dont-java-generics-support-primitive-types), but `:%s/double/float/` in `vim` will produce the `float` version (`:%s/double/long/` produces the `long` version). [Valhalla is a possible solution for this](https://openjdk.org/jeps/218)
 * Some `assert`s follow, thus document which arguments to use with this (without `-enableassertions`, thus are not enforced).
 * Some "Usage:" comments follow, which document how to use this.
 */
public class ImmutablePosBounds implements java.lang.Cloneable { /* Usage: replaces `double fooDistance; double fooFactor;` with `ImmutablePosBounds fooImmutablePosBounds;` */
&#9;/* Member variables &amp; constructors: */
&#9;public enum PosBoundsMode { // `PosBoundsMode` says how the sim must do `pos[dim] += dpos[dim]` (derivatives of positions).
&#9;&#9;invalidArgumentException, // `if(!isPosInBounds(pos)) { throw new IllegalArgumentException(); }`
&#9;&#9;wrapAroundResolution, // `pos[dim] = (getBounds()[dim] + pos[dim] + dpos[dim]) % getBounds()[dim];`.
&#9;&#9;clampToResolution, // `pos[dim] = Math.max(0, Math.min(getBounds()[dim] - 1, pos[dim] + dpos[dim]));`.
&#9;&#9;boundless, // `pos[dim] += dpos[dim];`.
&#9;}; // Notice: to teleport to new positions, `dpos[dim] = newPos[dim] - pos[dim]`. but most sims use relative motions.
&#9;protected PosBoundsMode posBounds;

&#9;protected Pos bounds = new Pos(0, 0);
&#9;protected Pos boundsSlash2 = bounds.slashScalar(2); // Improves execution of inner loops which use this
&#9;protected double boundsVolume = bounds.volume();
&#9;protected int gridResolution = 100;
&#9;protected int[] gridSize = computeGridSize();
&#9;public ImmutablePosBounds() {
&#9;&#9;setPosBoundsMode_(PosBoundsMode.boundless);
&#9;}
&#9;public ImmutablePosBounds(ImmutablePosBounds o) {
&#9;&#9;set_(o);
&#9;}
&#9;public ImmutablePosBounds(PosBoundsMode o, double[] resolution) {
&#9;&#9;setPosBoundsMode_(o);
&#9;&#9;setResolution_(resolution);
&#9;}
&#9;public ImmutablePosBounds(PosBoundsMode o, ImmutablePos resolution) {
&#9;&#9;setPosBoundsMode_(o);
&#9;&#9;setResolution_(resolution);
&#9;}
&#9;@Override
&#9;public PosBounds clone() { /* Usage: `ImmutablePosBounds pos = o.clone(); assert o.equals(pos);` */
&#9;&#9;return new PosBounds(this);
&#9;}
&#9;protected int[] computeGridSize() {
//&#9;&#9;int[] gridSize_ = bounds.clone();
&#9;&#9;int[] gridSize_ = new int[bounds.dims()];
&#9;&#9;for(int i = 0; gridSize_.length &gt; i; ++i) {
&#9;&#9;&#9;gridSize_[i] = (int)Math.ceil(getBounds(i) / gridResolution);
&#9;&#9;}
&#9;&#9;return gridSize_;
&#9;}

&#9;/* Public setter functions: `class PosBounds extends ImmutablePosBounds`. */
&#9;protected void setPosBoundsMode_(PosBoundsMode o) {
&#9;&#9;posBounds = o;
&#9;}
&#9;protected void setResolution_(double[] resolution) {
&#9;&#9;setBounds_(resolution);
&#9;&#9;boundsSlash2 = bounds.slashScalar(2);
&#9;&#9;boundsVolume = bounds.volume();
&#9;&#9;gridSize = computeGridSize();
&#9;}
&#9;protected void setResolution_(ImmutablePos resolution) {
&#9;&#9;setResolution_(resolution.pos);
&#9;}
&#9;protected void set_(ImmutablePosBounds o) {
&#9;&#9;setPosBoundsMode_(o.posBounds);
&#9;&#9;setResolution_(o.bounds);
&#9;&#9;setGridResolution_(o.gridResolution);
&#9;}
&#9;protected void setBounds_(double[] resolution) {
//&#9;&#9;assert null != resolution; // Notice: chose to use `java`'s default solution for `clone(null)`
&#9;&#9;bounds = new Pos(resolution);
&#9;}
&#9;protected void setBounds_(ImmutablePos resolution) {
&#9;&#9;bounds = resolution.clone();
&#9;}
&#9;protected void setBounds_(int index, double resolution) {
&#9;&#9;bounds.set(index, resolution);
&#9;}
&#9;protected void setBoundsSlash2_(double[] resolution) {
//&#9;&#9;assert null != resolution; // Notice: chose to use `java`'s default solution for `clone(null)`
&#9;&#9;boundsSlash2 = new Pos(resolution);
&#9;}
&#9;protected void setBoundsSlash2_(ImmutablePos resolution) {
&#9;&#9;boundsSlash2 = resolution.clone();
&#9;}
&#9;protected void setBoundsSlash2_(int index, double resolution) {
&#9;&#9;boundsSlash2.set(index, resolution);
&#9;}
&#9;protected void setGridResolution_(int resolution) {
&#9;&#9;gridResolution = resolution;
&#9;&#9;gridSize = computeGridSize();
&#9;}

&#9;/* Public getter functions: */
&#9;public PosBoundsMode getPosBoundsMode() { return this.posBounds; }
&#9;public double[] getBounds() {
&#9;&#9;assert null != bounds;
&#9;&#9;assert null != bounds.pos;
&#9;&#9;return bounds.pos; // Notice: should `return bounds.pos.clone()` to enforce "Immutable", but this is used in inner loops
&#9;}
&#9;public double[] getBoundsSlash2() { // Caches `getBounds()[dim] / 2` for physics uses (improves inner loops).
&#9;&#9;assert null != boundsSlash2;
&#9;&#9;assert null != boundsSlash2.pos;
&#9;&#9;return boundsSlash2.pos; // Notice: should `return boundsSlash2.pos.clone()` to enforce "Immutable", but this is used in inner loops
&#9;}
&#9;public ImmutablePos getBoundsPos() {
&#9;&#9;assert null != bounds; // Notice:  redundant (since `java` checks for `null` dereferences), but documents what this does
&#9;&#9;return bounds;
&#9;}
&#9;public double getBounds(int index) {
//&#9;&#9;assert null != bounds; // Notice: chose to use `java`'s default `throw`
//&#9;&#9;assert null != bounds.pos; // Notice: chose to use `java`'s default `throw`
&#9;&#9;return bounds.pos[index];
&#9;}
&#9;public ImmutablePos getBoundsSlash2Pos() { // Caches `getBounds().slash(2)` for physics uses (improves inner loops).
&#9;&#9;assert null != boundsSlash2; // Notice:  redundant (since `java` checks for `null` dereferences), but documents what this does
&#9;&#9;return boundsSlash2; // Notice: should `return boundsSlash2.clone()` to enforce "Immutable", but this is used in inner loops
&#9;}
&#9;public double getBoundsSlash2(int index) {
//&#9;&#9;assert null != bounds; // Notice: chose to use `java`'s default `throw`
//&#9;&#9;assert null != bounds.pos; // Notice: chose to use `java`'s default `throw`
&#9;&#9;return boundsSlash2.pos[index];
&#9;}
&#9;public double getBoundsVolume() {
&#9;&#9;return boundsVolume;
&#9;}
&#9;public int getGridSize(int index) {
&#9;&#9;return gridSize[index];
&#9;}
&#9;public int[] getGridSize() {
&#9;&#9;return gridSize.clone();
&#9;}

&#9;/* Comparison functions: */
&#9;@Override
&#9;public boolean equals(Object o) { /* Usage: `if(!equals(o)) { System.err.println("non-similar values"); }` */
&#9;&#9;if(null == o || !(o instanceof ImmutablePosBounds)) {
&#9;&#9;&#9;return false;
&#9;&#9;}
&#9;&#9;return equals((ImmutablePosBounds)o);
&#9;}
&#9;public boolean equals(ImmutablePosBounds o) { /* Usage: is `equals(Object o)` with low CPU use */
&#9;&#9;return o.posBounds == posBounds &amp;&amp;
&#9;&#9;&#9;o.bounds.equals(bounds) &amp;&amp;
&#9;&#9;&#9;o.gridResolution == gridResolution;
//&#9;&#9;&#9;o.boundsSlash2.equals(boundsSlash2) &amp;&amp;
//&#9;&#9;&#9;o.boundsVolume == boundsVolume &amp;&amp;
&#9;}
&#9;@Override
&#9;public int hashCode() { /* Usage: `if(hashCode() != hashCode(o)) { System.err.println("non-similar values"); }` */
&#9;&#9;return posBounds.hashCode() ^ bounds.hashCode() ^ gridResolution; // Notice: if `boundsSlash2` is independent of `bounds`, then this should include ` ^ boundsSlash2.hashCode()`
&#9;}

&#9;/* Public `ImmutablePosBounds` functions, which accept `double[]` (future versions also accept `ImmutablePos`): */
&#9;public String posOutOfBoundsStr(double[] pos, String posStr) {
&#9;&#9;return "`" + posStr + " = " + Arrays.toString(pos) + ";` `getBounds() = " + bounds.toString() + ";` (`grid = new ArrayList[" + gridSize[0] + "][" + gridSize[1] + "];`), so `" + posStr + "` is out of bounds.";
&#9;}
&#9;public String posOutOfBoundsStr(ImmutablePos pos, String posStr) {
&#9;&#9;return "`" + posStr + " = " + Arrays.toString(pos.pos) + ";` `getBounds() = " + bounds.toString() + ";` (`grid = new ArrayList[" + gridSize[0] + "][" + gridSize[1] + "];`), so `" + posStr + "` is out of bounds.";
&#9;}

&#9;public double[] posDiff(double[] pos, double[] o) {
&#9;&#9;if(PosBoundsMode.wrapAroundResolution == getPosBoundsMode()) {
&#9;&#9;&#9;double[] posDiff = new double[pos.length];
&#9;&#9;&#9;for(int i = 0; pos.length &gt; i; ++i) { /* Notice: ensure that `java` [unrolls this](https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/Physics_sims_which_structures_to_use.md#separate-variables-versus-dim-lists) */
&#9;&#9;&#9;&#9;posDiff[i] = pos[i] - o[i];
&#9;&#9;&#9;&#9;if(boundsSlash2.pos[i] &lt; posDiff[i]) {
&#9;&#9;&#9;&#9;&#9;posDiff[i] -= bounds.pos[i];
&#9;&#9;&#9;&#9;} else if(-boundsSlash2.pos[i] &gt; posDiff[i]) {
&#9;&#9;&#9;&#9;&#9;posDiff[i] += bounds.pos[i];
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;}
&#9;&#9;&#9;return posDiff;
&#9;&#9;} else {
&#9;&#9;&#9;return new double[] {pos[0] - o[0], pos[1] - o[1]};
&#9;&#9;}
&#9;}
&#9;public Pos posDiff(ImmutablePos pos, ImmutablePos o) {
&#9;&#9;Pos posDiff = pos.minus(o);
&#9;&#9;if(PosBoundsMode.wrapAroundResolution == getPosBoundsMode()) {
&#9;&#9;&#9;for(int i = 0; pos.pos.length &gt; i; ++i) { /* Notice: ensure that `java` [unrolls this](https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/Physics_sims_which_structures_to_use.md#separate-variables-versus-dim-lists) */
&#9;&#9;&#9;&#9;if(boundsSlash2.pos[i] &lt; posDiff.pos[i]) {
&#9;&#9;&#9;&#9;&#9;posDiff.pos[i] -= bounds.pos[i];
&#9;&#9;&#9;&#9;} else if(-boundsSlash2.pos[i] &gt; posDiff.pos[i]) {
&#9;&#9;&#9;&#9;&#9;posDiff.pos[i] += bounds.pos[i];
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;}
&#9;&#9;}
&#9;&#9;return posDiff;
&#9;}

&#9;public boolean isPosInBounds(double[] pos) throws IllegalArgumentException {
&#9;&#9;if(bounds.dims() != pos.length) {
&#9;&#9;&#9;throw new IllegalArgumentException("`bounds.dims() != pos.length`");
&#9;&#9;} // TODO: If this test is used at the start of all `pos*()` functions, replace `[]` with `Pos2`, unless optimizer stores this.
&#9;&#9;for(int i = 0; i &lt; pos.length; i++) {
&#9;&#9;&#9;if(0 &gt; pos[i] || bounds.at(i) &lt;= pos[i]) {
&#9;&#9;&#9;&#9;return false;
&#9;&#9;&#9;}
&#9;&#9;} // TODO: replace `for(...) {...}` with `switch(pos.length) { case 2: ... }`, unless optimizer does this.
&#9;&#9;return true;
&#9;}
&#9;public boolean isPosInBounds(ImmutablePos pos) throws IllegalArgumentException {
&#9;&#9;if(getBoundsPos().dims() != pos.dims()) {
&#9;&#9;&#9;throw new IllegalArgumentException("`getBoundsPos().dims() != pos.dims()`");
&#9;&#9;} // TODO: If this test is used at the start of all `pos*()` functions, replace `[]` with `Pos2`, unless optimizer stores this.
&#9;&#9;for(int i = 0; i &lt; pos.dims(); i++) {
&#9;&#9;&#9;if(0 &gt; pos.pos[i] || bounds.pos[i] &lt;= pos.pos[i]) {
&#9;&#9;&#9;&#9;return false;
&#9;&#9;&#9;}
&#9;&#9;} // TODO: replace `for(...) {...}` with +`ImmutablePos::isInBounds(ImmutablePos bounds)`, which can use `switch(pos.length) { case 2: ... }` or specialized virtual functions
&#9;&#9;return true;
&#9;}

&#9;public boolean posBound(double[] pos) throws IllegalArgumentException { // If `PosBoundsMode.boundless != posBounds`, this ensures the invariant `0 &lt;= pos[dim] &amp;&amp; FishSim.getBounds()[dim] &gt; pos[dim]` is established.
&#9;&#9;switch(getPosBoundsMode()) { // `PosBoundsMode.` is omitted from all `case`s, to support old `java --source` versions
&#9;&#9;case invalidArgumentException:
&#9;&#9;&#9;if(!isPosInBounds(pos)) {
&#9;&#9;&#9;&#9;throw new IllegalArgumentException(posOutOfBoundsStr(pos, "double[] pos"));
&#9;&#9;&#9;&#9;// return false; // Notice: unsure of codeflow after the exception is handled. This gives an error if uncommented, but without this, if the exception is handled, the function will fall through to `return true`.
&#9;&#9;&#9;}
&#9;&#9;&#9;break;
&#9;&#9;case wrapAroundResolution:
&#9;&#9;&#9;pos[0] = ((pos[0] % bounds.pos[0]) + bounds.pos[0]) % bounds.pos[0];
&#9;&#9;&#9;pos[1] = ((pos[1] % bounds.pos[1]) + bounds.pos[1]) % bounds.pos[1];
&#9;&#9;&#9;break;
&#9;&#9;case clampToResolution:
&#9;&#9;&#9;pos[0] = Math.max(0, Math.min(bounds.pos[0] - 1, pos[0])); // TODO: if `java` does not precompute `bounds.at(dim) - 1`, store `boundsMinus1[]`
&#9;&#9;&#9;pos[1] = Math.max(0, Math.min(bounds.pos[1] - 1, pos[1]));
&#9;&#9;&#9;break;
&#9;&#9;case boundless:
&#9;&#9;&#9;return isPosInBounds(pos);
&#9;&#9;default:
&#9;&#9;&#9;throw new IllegalArgumentException("Unknown `enum PosBoundsMode`: " + posBounds); // [The compiler does this for you](https://codingtechroom.com/question/what-exception-compiler-unknown-enum-values-switch-expressions), so this just serves to document the lack of `default` codeflow.
&#9;&#9;}
&#9;&#9;return true;
&#9;}
&#9;public boolean posBound(Pos pos) throws IllegalArgumentException { // If `PosBoundsMode.boundless != posBounds`, this ensures the invariant `0 &lt;= pos[dim] &amp;&amp; FishSim.getBounds()[dim] &gt; pos[dim]` is established.
&#9;&#9;assert getBoundsPos().dims() == pos.dims(); // Notice: `bounds` for volumetric physics allows 2-dimensional `pos`, but `posBounds` does not implement such codeflow
&#9;&#9;switch(getPosBoundsMode()) { // `PosBoundsMode.` is omitted from all `case`s, to support old `java --source` versions
&#9;&#9;case invalidArgumentException:
&#9;&#9;&#9;if(!isPosInBounds(pos)) {
&#9;&#9;&#9;&#9;throw new IllegalArgumentException(posOutOfBoundsStr(pos, "Pos pos"));
&#9;&#9;&#9;&#9;// return false; // Notice: unsure of codeflow after the exception is handled. This gives an error if uncommented, but without this, if the exception is handled, the function will fall through to `return true`.
&#9;&#9;&#9;}
&#9;&#9;&#9;break;
&#9;&#9;case wrapAroundResolution:
&#9;&#9;&#9;pos.plusEquals(getBoundsPos());
&#9;&#9;&#9;pos.moduloEquals(getBoundsPos());
&#9;&#9;&#9;break;
&#9;&#9;case clampToResolution:
&#9;&#9;&#9;pos.pos[0] = Math.max(0, Math.min(getBounds()[0] - 1, pos.pos[0])); // TODO: +`Pos::{maxEquals(Pos), minEquals(Pos)}` or +`Pos::clamp(pos)`
&#9;&#9;&#9;pos.pos[1] = Math.max(0, Math.min(getBounds()[1] - 1, pos.pos[1]));
&#9;&#9;&#9;break;
&#9;&#9;case boundless:
&#9;&#9;&#9;return isPosInBounds(pos);
&#9;&#9;default:
&#9;&#9;&#9;throw new IllegalArgumentException("Unknown `enum PosBoundsMode`: " + posBounds); // [The compiler does this for you](https://codingtechroom.com/question/what-exception-compiler-unknown-enum-values-switch-expressions), so this just serves to document the lack of `default` codeflow.
&#9;&#9;}
&#9;&#9;return true;
&#9;}

};</code></pre></div><div><hr></div><h3><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/PosBounds.java">./susuwu/PosBounds.java</a></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;a45c3247-a9ee-48fd-81c7-2c68c4852343&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; stores the newest version of `./susuwu/PosBounds.java` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

package susuwu; /* Usage: `import susuwu.PosBounds;` */
/**
 * `class PosBounds` says how sims enforce position bounds.
 * Usage: `PosBounds posBounds;`
 * `class PosBounds` does not use generics since [`java` generics do not allow primitives](https://stackoverflow.com/questions/2721546/why-dont-java-generics-support-primitive-types), but `:%s/double/float/` in `vim` will produce the `float` version (`:%s/double/long/` produces the `long` version). [Valhalla is a possible solution for this](https://openjdk.org/jeps/218)
 * Some `assert`s follow, thus document which arguments to use with this (without `-enableassertions`, thus are not enforced).
 * Some "Usage:" comments follow, which document how to use this.
 */
public class PosBounds extends ImmutablePosBounds { /* Usage: replaces `double fooDistance; double fooFactor;` with `PosBounds fooPosBounds;` */
&#9;/* Member variables &amp; constructors: */
&#9;public PosBounds() {
&#9;&#9;setPosBoundsMode_(PosBoundsMode.boundless);
&#9;}
&#9;public PosBounds(ImmutablePosBounds o) {
&#9;&#9;set_(o);
&#9;}
&#9;public PosBounds(PosBoundsMode o, double[] resolution) {
&#9;&#9;setPosBoundsMode_(o);
&#9;&#9;setResolution_(resolution);
&#9;}
&#9;public PosBounds(PosBoundsMode o, ImmutablePos resolution) {
&#9;&#9;setPosBoundsMode_(o);
&#9;&#9;setResolution_(resolution);
&#9;}
&#9;@Override
&#9;public PosBounds clone() { /* Usage: `PosBounds pos = o.clone(); assert o.equals(pos);` */
&#9;&#9;return new PosBounds(this);
&#9;}

&#9;/* Public setter functions: `class PosBounds extends PosBounds`. */
&#9;public void setPosBoundsMode(PosBoundsMode o) {
&#9;&#9;setPosBoundsMode_(o);
&#9;}
&#9;public void set(ImmutablePosBounds o) {
&#9;&#9;set_(o);
&#9;}
&#9;public void setBounds(double[] resolution) {
&#9;&#9;setResolution_(resolution);
&#9;}
&#9;public void setBounds(int index, double resolution) {
&#9;&#9;bounds.set(index, resolution);
&#9;&#9;boundsSlash2.set(index, resolution / 2);
&#9;&#9;boundsVolume = bounds.volume();
&#9;&#9;gridSize[index] = (int)Math.ceil(bounds.at(index) / gridResolution);
&#9;}
&#9;public void setGridResolution(int resolution) {
&#9;&#9;setGridResolution_(resolution);
&#9;}
};</code></pre></div><div><hr></div><h3><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/SimUsages.java">./susuwu/SimUsages.java</a></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;6c2eac5a-51fb-48be-8242-6cf68ae72d83&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; has the newest version of `./susuwu/SimUsages.java` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

package susuwu; /* Usage: `import susuwu.SimUsages;` */

import javafx.application.Platform; /* `Platform.runLater(...)` */
import javafx.scene.layout.Pane; /* `Pane pane` */
import javafx.scene.paint.Color; /* `Color.WHITE` */
import javafx.scene.text.Text; /* `Text fpsText` */

/**
 * {@code class SimUsages} shows {@code FpsTextMode} statistics such as {@code fps} or {@code ms}.
 * Requirements: some render loop (for measurements). Is not specific to the renderer used.
 * Was produced for {@code susuwu.FishSim}, so the text (plus comments) assume the organisms are {@code class Fish}, but {@code SimUsages} is not specific to {@code class Fish}
 * Some {@code assert}s follow, thus document which arguments to use with this (without {@code -enableassertions}, thus are not enforced).
 * Some "Usage:" comments follow, which document how to use this.
 * Usage: {@code SimUsages usages = SimUsages(Pane); usages.show(); usages.fpsTextMode = FpsTextMode.fps.value | FpsTextMode.ms.value;}
 */
public class SimUsages {
&#9;/* `public` members */
&#9;public long fpsTextMode = FpsTextMode.allUsages.value; // Usage `usages.fpsTextModeFps = FpsTextMode.fps.value;` to just show `fps`
&#9;public double secondsPerFpsTextRefresh = 1.0; // Usage: `usages.secondsPerFpsTextRefresh = 0.2;` to give more current values, or `= 2.0;` to give more smooth values. In `postRefresh()`: if `secondsPerFpsTextRefresh` elapses, `lastTime = System.nanoTime(); fpsTextRefresh();``
&#9;public double renderMs = Double.NaN; // Usage: `functionWhichUsesRenderMs(usages.renderMs);`. Stores average **ms** from `startRender()` to `postRender()` (`renderNs / renderCounter / 1_000_000.0`)
&#9;public double physicsMs = Double.NaN; // Usage: `functionWhichUsesPhysicsMs(usages.physicsMs);`. Stores average **ms** from `startPhysics()` to `postPhysics()` (`physicsNs / physicsCounter / 1_000_000.0`)
&#9;public double fps = Double.NaN; // Usage: `functionWhichUsesFps(usages.fps);`. Stores `renderCounter / (System.nanoTime() - lastTime) / 1_000_000_000.0`

&#9;/* `private` or almost-`private` members */
&#9;public long lastTime = System.nanoTime(); // Stores `System.nanoTime()` when `renderCounter = 0`.
&#9;public int refreshCounter = 0; // Sum of `postRefresh()` uses since `SimUsages(Pane)`.
&#9;public int renderCounter = 0; // Sum of `postRender()` uses since `lastTime = System.nanoTime()`.
&#9;public int physicsCounter = 0; // Sum of `postPhysics()` uses since `lastTime = System.nanoTime()`.
&#9;public long physicsNs = -1; // Sum of `nanoTime()` at `postRender()` minus `nanoTime()` at `startRender()` since `lastTime = System.nanoTime()`.
&#9;public long renderNs = -1; // Sum of `nanoTime()` at `postRender()` minus `nanoTime()` at `startRender()` since `lastTime = System.nanoTime()`.
&#9;private Pane root;
&#9;public SimUsages(Pane pane) {
&#9;&#9;this.root = pane;
&#9;&#9;this.root.getChildren().add(fpsText);
&#9;}
&#9;static public enum FpsTextMode { // `FpsTextMode` says which resources `fpsText` will show.
&#9;&#9;none      (0     ), // `fpsText = "";`
&#9;&#9;fps       (1 &lt;&lt; 0), // `fpsText` += `fps` "FPS";
&#9;&#9;ms        (2 &lt;&lt; 1), // `fpsText` += `ms` "ms"; /* Notice: `ms = 1000 / fps;`, so includes idle CPU */
&#9;&#9;msSpec    (1 &lt;&lt; 2), // `fpsText` += `renderMs` "renderMs," `physicsMs` "physicsMs"; /* Notice: uses `System.nanoTime()`, does not include idle CPU */
&#9;&#9;msFish    (1 &lt;&lt; 3), // `fpsText` += `renderMs / fishShown` "renderMs / Fish shown," `physicsMs / fishListSize` "physicsMs / Fish";
&#9;&#9;fish      (1 &lt;&lt; 4), // `fpsText` += `fishListSize` "Fish";
&#9;&#9;fishShown (1 &lt;&lt; 5), // `fpsText` += `fishShown` "Fish shown";
&#9;&#9;allUsages (FpsTextMode.fps.value | FpsTextMode.ms.value | FpsTextMode.msSpec.value | FpsTextMode.msFish.value | FpsTextMode.fish.value | FpsTextMode.fishShown.value); // shows as all supported usages
&#9;&#9;long value; // Stores bitwise-or of those.
&#9;&#9;FpsTextMode(long value) { this.value = value; }
&#9;}; // TODO: replace manual bitshifts with `java.util.EnumSet&lt;E&gt;`?

&#9;private Text fpsText = new Text("0 FPS");
&#9;public void show() {
&#9;&#9;fpsText.setX(10);
&#9;&#9;fpsText.setY(30);
&#9;&#9;fpsText.setFill(Color.WHITE);
&#9;}

&#9;/* Measurement funtions
&#9; * Usage: ```
&#9; * refreshLoop() {
&#9; * &#9;usages.startRefresh();
&#9; * &#9;usages.startPhysics(); physics(fishList);   usages.postPhysics();
&#9; * &#9;usages.startRender();  render(visibleFish); usages.postRender();
&#9; * &#9;usages.postRefresh(System.nanoTime(), visibleFish.size(), fishList.size());
&#9; * }```
&#9; * "Refresh loop" is the loop which invokes the render loop plus the physics loop. `class SimUsages` does not assume that the physics loop is invoked as often as the render loop is. If the renderer has its own separate loop, the render loop is the refresh loop.
&#9; */
&#9;public void startRefresh() { // Usage: `startRender();` at start of refresh loop.
&#9;}
&#9;public void postRefresh(long now, long fishShown, long fishListSize) { // Usage: `postRefresh(System.nanoTime(), visibleObjects.size(), physisObjects.size());`
&#9;&#9;double elapsed = (now - lastTime) / 1_000_000_000.0;
&#9;&#9;if(elapsed &gt;= secondsPerFpsTextRefresh) {
&#9;&#9;&#9;lastTime = now;
&#9;&#9;&#9;fps = renderCounter / elapsed;
&#9;&#9;&#9;renderMs = renderNs / renderCounter / 1_000_000.0;
&#9;&#9;&#9;physicsMs = physicsNs / physicsCounter / 1_000_000.0;
&#9;&#9;&#9;Platform.runLater(() -&gt; fpsTextRefresh(fishShown, fishListSize));
&#9;&#9;&#9;renderCounter = 1;
&#9;&#9;&#9;renderNs = -1;
&#9;&#9;&#9;physicsCounter = 1;
&#9;&#9;&#9;physicsNs = -1;
&#9;&#9;}
&#9;&#9;refreshCounter++;
&#9;}
&#9;private long renderNsStart, physicsNsStart;
&#9;public void startRender() { // Usage: `startRender();` at start of render loop
&#9;&#9;renderNsStart = System.nanoTime();
&#9;}
&#9;public void preSynchro() { // Usage: `startRender(); ... startSynchro(); SdlGles2.glClear(...); postSynchro();`
&#9;&#9;renderNsStart += System.nanoTime();
&#9;} // TODO: reduce `preSynchro()` to no-op, improve `postSynchro()` to subtract actual Virtual Synchronization time from `renderNsStat` (so the time to clear buffers is included).
&#9;public void postSynchro() { // Usage: `startRender(); ... startSynchro(); SdlGles2.glClear(...); postSynchro();`
&#9;&#9;renderNsStart -= System.nanoTime(); // Purpose: subtracts vertical synchronization time from "drawMS` (which is computed from `renderNs`, which is computed from `renderNsStart`). Problem: `glClear()` does not just wait for Virtical Synchronization, but also clears the buffer, which this will also subtract from `renderNs`.
&#9;}
&#9;public void postRender() { // Usage: `startRender();` at closure of render loop
&#9;&#9;renderNs += System.nanoTime() - renderNsStart;
&#9;&#9;renderCounter++;
&#9;}
&#9;public void startPhysics() { // Usage: `startPhysics();` at start of physics loop
&#9;&#9;physicsNsStart = System.nanoTime();
&#9;}
&#9;public void postPhysics() { // Usage: `startPhysics();` at closure of physics loop
&#9;&#9;physicsNs += System.nanoTime() - physicsNsStart;
&#9;&#9;physicsCounter++;
&#9;}

&#9;private void fpsTextRefresh(long fishShown, long fishListSize) { /* Usage: `usages.fpsTextModeFps(visibleFish.size(), fishList.size())` */
&#9;&#9;boolean fpsTextModeFps = (0 != (FpsTextMode.fps.value &amp; fpsTextMode));
&#9;&#9;boolean fpsTextModeMs = (0 != (FpsTextMode.ms.value &amp; fpsTextMode));
&#9;&#9;boolean fpsTextModeMsSpec = (0 != (FpsTextMode.msSpec.value &amp; fpsTextMode));
&#9;&#9;boolean fpsTextMsSpecFish = (0 != ((FpsTextMode.msSpec.value | FpsTextMode.msFish.value) &amp; fpsTextMode)); // TODO: replace manual bitshifts with `java.util.EnumSet&lt;E&gt;`?
&#9;&#9;boolean fpsTextModeMsFish = (0 != (FpsTextMode.msFish.value &amp; fpsTextMode));
&#9;&#9;boolean fpsTextModeFish = (0 != (FpsTextMode.fish.value &amp; fpsTextMode));
&#9;&#9;boolean fpsTextModeFishShown = (0 != (FpsTextMode.fishShown.value &amp; fpsTextMode));
&#9;&#9;double totalMs = 1 / fps * 1000;
&#9;&#9;String fpsTextStr = "";
&#9;&#9;String strSep = ", ", strJoin = " (";
&#9;&#9;if(FpsTextMode.none.value == fpsTextMode) { return; }
&#9;&#9;if(fpsTextModeFps) {
&#9;&#9;&#9;fpsTextStr += String.format("%4.2f FPS" + strSep, fps);
&#9;&#9;}
&#9;&#9;if(fpsTextModeMs) {
&#9;&#9;&#9;fpsTextStr += String.format("%4.2f MS" + (fpsTextMsSpecFish ? strJoin : strSep), totalMs);
&#9;&#9;}
&#9;&#9;if(fpsTextModeMsSpec) {
&#9;&#9;&#9;fpsTextStr += String.format("%4.2f drawMS, %4.2f physicsMS", renderMs, physicsMs);
&#9;&#9;&#9;fpsTextStr += (fpsTextModeMsFish ? strSep : "");
&#9;&#9;}
&#9;&#9;if(fpsTextModeMsFish) {
&#9;&#9;&#9;fpsTextStr += String.format("%2.4f drawMS / Fish shown, %2.4f physicsMS / Fish", renderMs / fishShown, physicsMs / fishListSize);
&#9;&#9;}
&#9;&#9;if(fpsTextMsSpecFish) {
&#9;&#9;&#9;if(0 != ((FpsTextMode.ms.value &amp; fpsTextMode))) {
&#9;&#9;&#9;&#9;fpsTextStr += ")";
&#9;&#9;&#9;}
&#9;&#9;&#9;fpsTextStr += strSep;
&#9;&#9;}
&#9;&#9;if(fpsTextModeFish) {
&#9;&#9;&#9;fpsTextStr += String.format("%4d Fish", fishListSize);
&#9;&#9;&#9;fpsTextStr += (fpsTextModeFishShown ? strJoin : strSep);
&#9;&#9;}
&#9;&#9;if(fpsTextModeFishShown) {
&#9;&#9;&#9;fpsTextStr += String.format(fpsTextModeFish ? "%4d shown)" : "%4d Fish shown", fishShown);
&#9;&#9;&#9;fpsTextStr += strSep;
&#9;&#9;}
&#9;&#9;fpsText.setText(fpsTextStr.substring(0, fpsTextStr.length() - strSep.length()));
&#9;}
};</code></pre></div><div><hr></div><h3><a href="https://github.com/SwuduSusuwu/SusuJava/blob/pos2_PosBounds/susuwu/FishSim.java">./susuwu/FishSim.java</a></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;2886984c-45df-412b-9201-500817df5aea&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/* Attribution (henceforth "*this attribution*", whose syntax is *Markdown*): 2024 [Swudu Susuwu](https://swudususuwu.substack.com)
 * &lt;https://github.com/SwuduSusuwu/SusuJava/&gt; has the newest version of `./susuwu/FishSim.java` (henceforth "*this source code*").
 * If *this attribution* is shown, *this source code* allows all uses. *This attribution* constitutes the most permissive which is compatible with [*GPLv2*](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) + [*Apache 2*](https://www.apache.org/licenses/LICENSE-2.0.html), which is suitable for personal use (also suitable for school use).
 * If *this attribution* is not professional enough for business use: businesses can use *this source code* through included versions of [*GPLv2*](./LICENSE_GPLv2), [*Apache 2*](./LICENSE), or through both of those.
 */

package susuwu; /* Usage: `import susuwu.FishSim;` */

import javafx.animation.Animation;
import javafx.animation.AnimationTimer;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
import susuwu.SimUsages; /* `class SimUsages`, `enum FpsTextMode` */
import susuwu.Calculus; /* `Calculus.pow2()` */
import susuwu.Forces; /* `class Forces implements java.lang.Cloneable` */
import susuwu.ImmutablePosBounds; /* `enum PosBoundsMode`: which stores how sims enforce bounds. */
import susuwu.PosBounds; /* `class PosBounds : extends ImmutablePosBounds`, `PosBounds.set*(PosBounds*)` */
import susuwu.ImmutablePos; /* `class ImmutablePos implements java.util.RandomAccess` */
import susuwu.Pos; /* `class Pos extends ImmutablePos` */
import susuwu.Pos2; /* `class Pos extends Pos` */
//    public static class Pos2 extends double[2] {} // `{Pos2[0], Pos2[1]}` is `{x, y}` position (or resolution), or is `{pos[0], pos[0]}` motion (derivative of position), or is is `{d2x, d2y}` acceleration (derivative number 2). This was supposed to do what `typedef` does (wish for future-proof (limitless dimensions) virtual `class` with functions for numerous transforms).
// TODO: test how much of `java`'s [static `Array` overhead](https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/Physics_sims_which_structures_to_use.md#separate-variables-versus-dim-lists) `java`'s toolkit optimizes for you. If performance is a problem, choose a new approach to use.

/**
 * Simple [*JavaFX*](https://github.com/openjdk/jfx) fish sim, which includes reusable {@code public class}s (for new sims to use). Most of the reusable {@code public class}s are in other {@code .java} sources for {@code package susuwu}
 * This ([`./susuwu/FishSim.java`](./FishSim.java)) uses pseudo-*Markdown* for comments, but [`./posts/FishSim.md`](../posts/FishSim.md) is the actual [*Markdown*](https://github.github.com/gfm/) document for this.
 * Notice: replaced most of [*Solar-Pro-2*'s original `FishSim.java`](https://github.com/SwuduSusuwu/SusuJava/blob/solarPro2FishSim/susuwu/FishSim.java), as [`./posts/FishSim.md#intro`](../posts/FishSim.md#intro) documents (plus [*GitHub*'s `/compare/` tool shows](https://github.com/SwuduSusuwu/SusuJava/compare/solarPro2FishSim..susuFishSim#diff-8c440bb92bc6939e1450542897e0bbb1a8737b93808ea63ed32784edfacef4b4).
 */
public class FishSim extends Application {
&#9;public enum PhysicsMode { // `PhysicsMode` says how to execute `updateFish()`
&#9;&#9;synchronousHomo,      // `updateFish()` once per `refreshLoop()`.
&#9;&#9;synchronousInterval,  // `updateFish()` per `positionInterval` `refreshLoop()`s.
&#9;&#9;asynchronousHomo,     // `executor.submit(() -&gt; updateFish());` once per `refreshLoop()`.
&#9;&#9;asynchronousInterval, // `executor.submit(() -&gt; updateFish());` per `positionInterval` `refreshLoop()`s.
&#9;&#9;separateUnbound,      // `new AnimationTimer() { public void handle(long now) { updateFish(); }` // Notice: with `GLES2` this has an implicit bound to the monitor refresh (Virtual Synchronization, which `SimUsages` does not count towards "drawMS").
&#9;&#9;separateFps,          // `Timeline timeline = new Timeline( new KeyFrame(Duration.millis(1000.0 / physicsRefreshHertz), event -&gt; { updateFish(); })`
&#9;}
&#9;private static PhysicsMode monitorRefreshMode = PhysicsMode.separateFps; // `monitorRefreshMode` must use `.separateUnbound` or `.separateFps`.
&#9;private static PhysicsMode physicsMode = PhysicsMode.separateFps; // Notice: if `PhysicsMode.*Interval`, must set `positionInterval`. if `PhysicsMode.separateFps`, must set `physicsRefreshHertz`.

&#9;static ReentrantLock renderFishLock = new ReentrantLock();
&#9;static ReentrantLock updateFishLock = new ReentrantLock();
&#9;// TODO: remove `static` from {`resolution`, `bounds`}, to allow to remove `static` from {`setResolution()`, `renderFishLock, `updateFishLock`}, so `FishSim` allows numerous windows
&#9;public static boolean setResolution(int[] newResolution) { /* Notice: invalidates references to old `bounds`, `*Slash2` addresses. */ // TODO: remove `static` from {`resolution`, `bounds`}, to allow to remove `static` from {`setResolution()`, `renderFishLock, `updateFishLock`}, so `FishSim` allows numerous windows
&#9;&#9;assert 2 == newResolution.length;
&#9;&#9;assert 0 &lt; newResolution[0]; //TODO: allow "headless" instances with `resolution = {0, 0}`?
&#9;&#9;assert 0 &lt; newResolution[1];
&#9;&#9;updateFishLock.lock();
&#9;&#9;renderFishLock.lock();
&#9;&#9;resolution = newResolution;
&#9;&#9;resolutionf.pos[0] = resolution[0]; resolutionf.pos[1] = resolution[1];
&#9;&#9;resolutionfSlash2 = resolutionf.slashScalar(2); /* Notice: invalidates references which store the old address to `resolutionfSlash2`. */
&#9;&#9;resVolume = (int)Math.round(resolutionf.volume()); /* Notice: uses `Pos::volume()` since simple source code is less bug prone. `Math.round` ensures 24-bit mantissas give accurate values */
&#9;&#9;posBounds.setBounds(resolutionf.starScalar(boundsResolutionFactor).pos); // TODO: if sure that no functions store references to the original instance, replace the above row with this (since simple source code is less bug prone)
&#9;&#9;// canvas = new Canvas(resolution[0], resolution[1]); // TODO: replace with `canvas.setWidth(resolution[0]); canvas.setHeight(resolution[1]);`?
&#9;&#9;// gc = canvas.getGraphicsContext2D();
&#9;&#9;// scene = new Scene(root, resolution[0], resolution[1], Color.LIGHTBLUE); // replace with `scene.widthProperty().bind(primaryStage.widthProperty());`?
&#9;&#9;// stage.setScene(scene);
&#9;&#9;renderFishLock.unlock();
&#9;&#9;updateFishLock.unlock();
&#9;&#9;return true;
&#9;}
&#9;private static int[] resolution = {1280, 720};
&#9;private static Pos2 resolutionf = new Pos2(resolution[0], resolution[1]);
&#9;private static Pos resolutionfSlash2 = resolutionf.slashScalar(2); /* Improves execution of inner loops which use this. Notice: `setResolution(newResolution)` invalidates stored references to `resolutionfSlash2` */
&#9;private static int resVolume = (int)Math.round(resolutionf.volume()); /* Notice: uses `Pos::volume()` since simple source code is less bug prone. `Math.round` ensures 24-bit mantissas give accurate values */
&#9;private static double boundsResolutionFactor = (1_000_000 &gt; resVolume ? 2.0 : 1.2); // Ocean is `resolution[dim] * boundsResolutionFactor`. `2.0` gives more room for natural oceans, but old laptops with huge resolutions (such as `{2200, 1200}`) must use `1.2` so the load is low enough for old CPUs to process). TODO: include short benchmark (on startup) to set `boundsResolutionFactor` to optimal value, or reduce CPU use for unshown `Fish` (`if(!fish.isVisible)`, then execute `updateFish` just once per second (1 hertz), with larger steps).
&#9;private static PosBounds posBounds = new PosBounds(PosBounds.PosBoundsMode.wrapAroundResolution, resolutionf.starScalar(boundsResolutionFactor)); // for simple sims, use `resolutionf.clone()`
&#9;private static double fishVolume = 200; // Uses resolution of `Fish::render()`.
&#9;private static double fishLengthsSep = 62; // Average `Fish`-lengths distance  from `Fish` to `Fish`.
&#9;private static double fishPerVolume = 1 / fishVolume / fishLengthsSep; // `Fish` per volume (for 2D, volume is resolution).
&#9;private static int fishCount = (int)(posBounds.getBoundsVolume() * fishPerVolume);
&#9;private static int gridResolution = 100; // Notice: set this to `Colllections.max({forces*.distance})` (which should equal what most sims call "view distance"), so that all relevent `Fish` are processed.
&#9;private static int positionInterval = 2; // The `refreshCounter` per `Fish::applyFlockingRulesUpdate()`
&#9;public static double monitorRefreshHertz = 60.0; // The `SimUsages.fps` to wish for // Notice: since this limits `SimUsages.fps` to `monitorRefreshHertz`, this prevents benchmarks which use `FpsTextMode.fps` (or `FpsTextMode.ms`). Benchmarks can still use `FpsTextMode.msSpec` (or `FpsTextMode.msFish`).
&#9;public static double physicsRefreshHertz = monitorRefreshHertz / positionInterval; // The `1 / SimUsages.physicsMs` to wish for // Notice: unknown what `javafx.animation.Timeline` does if `physicsRefreshHertz &gt; (1 / SimUsages.physicsMs)`, but guess thus stalls or consumes multiple executors

&#9;private List&lt;Fish&gt; fishList = new ArrayList&lt;&gt;();
&#9;private int fishShown = 0;
&#9;private List&lt;Fish&gt;[][] grid; /* `listToPartitions(List&lt;&gt;[][] grid, List&lt;&gt; list)` uses this */
&#9;private Random random = new Random();
&#9;private Pane root = new Pane();
&#9;private Canvas canvas = new Canvas(resolution[0], resolution[1]);
&#9;private GraphicsContext gc = canvas.getGraphicsContext2D();
&#9;private Stage stage;
&#9;SimUsages simUsages = new SimUsages(root);
//&#9;simUsages.fpsTextMode = FpsTextMode.allUsages.value; // TODO: "error: &lt;identifier&gt; expected" solution

&#9;private ExecutorService executor = Executors.newSingleThreadExecutor();

&#9;public static void main(String[] args) {
&#9;&#9;launch(args);
&#9;}

&#9;@Override
&#9;public void start(Stage primaryStage) {
&#9;&#9;// Initialize fish
&#9;&#9;for(int i = 0; i &lt; fishCount; i++) {
&#9;&#9;&#9;Pos2 pos = new Pos2(random.nextDouble() * posBounds.getBounds(0), random.nextDouble() * posBounds.getBounds(1));
&#9;&#9;&#9;Pos2 dpos = new Pos2((random.nextDouble() * 2 - 1) * Fish.dposMax, (random.nextDouble() * 2 - 1) * Fish.dposMax);
&#9;&#9;&#9;fishList.add(new Fish(pos, dpos, Color.color(random.nextDouble(), random.nextDouble(), random.nextDouble())));
&#9;&#9;}

&#9;&#9;root.getChildren().add(canvas);

&#9;&#9;Scene scene = new Scene(root, resolution[0], resolution[1], Color.LIGHTBLUE);
&#9;&#9;stage = primaryStage;
&#9;&#9;primaryStage.setScene(scene);
&#9;&#9;primaryStage.setTitle("Fish Simulation (Boids)");
&#9;&#9;primaryStage.setResizable(false);
&#9;&#9;primaryStage.show();
&#9;&#9;simUsages.show();

&#9;&#9;posBounds.setGridResolution(gridResolution);
&#9;&#9;grid = new ArrayList[posBounds.getGridSize(0)][posBounds.getGridSize(1)]; /* `listToPartitions(List&lt;&gt;[][] grid, List&lt;&gt; list)` uses this */
&#9;&#9;for(int i = 0; i &lt; grid.length; i++) {
&#9;&#9;&#9;for(int j = 0; j &lt; grid[i].length; j++) {
&#9;&#9;&#9;&#9;grid[i][j] = new ArrayList&lt;&gt;();
&#9;&#9;&#9;}
&#9;&#9;}

&#9;&#9;// Start animation loop
&#9;&#9;switch(monitorRefreshMode) { // `PhysicsMode.` is omitted from all `case`s, to support old `java --source` versions
&#9;&#9;case separateUnbound:
&#9;&#9;&#9;new AnimationTimer() {
&#9;&#9;&#9;&#9;@Override
&#9;&#9;&#9;&#9;public void handle(long now) { refreshLoop(now); }
&#9;&#9;&#9;}.start(); // Notice: replace `Timeline` with this for benchmarks which use `FpsTextMode.fps` (or `FpsTextMode.ms`).
&#9;&#9;&#9;break;
&#9;&#9;case separateFps:
&#9;&#9;&#9;Timeline timeline = new Timeline(
&#9;&#9;&#9;&#9;new KeyFrame(Duration.millis(1000.0 / monitorRefreshHertz), event -&gt; { refreshLoop(System.nanoTime()); })
&#9;&#9;&#9;); // Notice: since this limits `SimUsages.fps` to `monitorRefreshHertz`, this prevents benchmarks which use `FpsTextMode.fps` (or `FpsTextMode.ms`). Benchmarks can still use `FpsTextMode.msSpec` (or `FpsTextMode.msFish`).
&#9;&#9;&#9;timeline.setCycleCount(Animation.INDEFINITE);
&#9;&#9;&#9;timeline.play();
&#9;&#9;&#9;break;
&#9;&#9;default:
&#9;&#9;&#9;throw new IllegalArgumentException("Unsupported `PhysicsMode monitorRefreshMode`: " + monitorRefreshMode);
&#9;&#9;}
&#9;&#9;switch(physicsMode) { // `PhysicsMode.` is omitted from all `case`s, to support old `java --source` versions
&#9;&#9;case separateUnbound: // Notice: `GLES2` version uses Vertical Synchronization, which `SimUsages` subtracts from `renderNs` (does not count towards resource usage).
&#9;&#9;&#9;new AnimationTimer() {
&#9;&#9;&#9;&#9;@Override
&#9;&#9;&#9;&#9;public void handle(long now) { updateFish(); }
&#9;&#9;&#9;}.start();
&#9;&#9;&#9;break;
&#9;&#9;case separateFps:
&#9;&#9;&#9;Timeline loopPerSecond = new Timeline(
&#9;&#9;&#9;&#9;new KeyFrame(Duration.millis(1000.0 / physicsRefreshHertz), event -&gt; { updateFish(); })
&#9;&#9;&#9;);
&#9;&#9;&#9;loopPerSecond.setCycleCount(Animation.INDEFINITE);
&#9;&#9;&#9;loopPerSecond.play();
&#9;&#9;&#9;break;
&#9;&#9;}
&#9;}

&#9;private void refreshLoop(long now) {
&#9;&#9;simUsages.startRefresh();
&#9;&#9;switch(physicsMode) { // `PhysicsMode.` is omitted from all `case`s, to support old `java --source` versions
&#9;&#9;case synchronousHomo:
&#9;&#9;&#9;updateFish();
&#9;&#9;&#9;break;
&#9;&#9;case synchronousInterval:
&#9;&#9;&#9;if(simUsages.refreshCounter % positionInterval == 0) {
&#9;&#9;&#9;&#9;updateFish();
&#9;&#9;&#9;}
&#9;&#9;&#9;break;
&#9;&#9;case asynchronousHomo:
&#9;&#9;&#9;executor.submit(() -&gt; updateFish());
&#9;&#9;&#9;break;
&#9;&#9;case asynchronousInterval:
&#9;&#9;&#9;if(simUsages.refreshCounter % positionInterval == 0) {
&#9;&#9;&#9;&#9;executor.submit(() -&gt; updateFish());
&#9;&#9;&#9;}
&#9;&#9;&#9;break;
&#9;&#9;case separateUnbound:
&#9;&#9;case separateFps:
&#9;&#9;&#9;break; // no-op for both, since `start(Stage primaryStage)` processes thus
&#9;&#9;default:
&#9;&#9;&#9;throw new IllegalArgumentException("Unsupported `PhysicsMode physicsMode`: " + physicsMode);
&#9;&#9;}

&#9;&#9;renderFish();
&#9;&#9;simUsages.postRefresh(now, fishShown, fishList.size());
&#9;}

&#9;private void outOfBounds(String function, Fish fish) {
&#9;&#9;/* Notice: `outOfBounds()` has numerous sensible actions other than to print to `stderr`: `fish.die()`, `fish.stop()`, `fish.reverse()`, `fish.wrapAround()` */
&#9;&#9;System.err.println(function + ": " + posBounds.posOutOfBoundsStr(fish.pos, "Fish.pos"));
&#9;}

&#9;/* Spatial partitioning (simple grid system). TODO: generic version of this (accept all `class`s with `#isInBounds` plus `#pos`). */
&#9;private void listToPartitions(List&lt;Fish&gt;[][] grid, List&lt;Fish&gt; list) {
&#9;&#9;assert grid.length == (int)Math.ceil(posBounds.getBounds(0) / gridResolution);
&#9;&#9;assert grid[0].length == (int)Math.ceil(posBounds.getBounds(1) / gridResolution);
&#9;&#9;for(int i = 0; i &lt; grid.length; i++) {
&#9;&#9;&#9;for(int j = 0; j &lt; grid[i].length; j++) {
&#9;&#9;&#9;&#9;grid[i][j].clear();
&#9;&#9;&#9;}
&#9;&#9;}
&#9;&#9;for(Fish fish : list) { /* Assign list members to grid sections */
&#9;&#9;&#9;if(fish.isInBounds) {
&#9;&#9;&#9;&#9;int[] gridPos = {(int) (fish.pos.pos[0] / gridResolution), (int) (fish.pos.pos[1] / gridResolution)};
&#9;&#9;&#9;&#9;grid[gridPos[0]][gridPos[1]].add(fish); // if `gridPos` is not in bounds, this will `throw new IndexOutOfBoundsException()`. But `Fish.setPos()` uses `PosBounds::posBounds.posBound()` which, which ensures `Fish.pos` bounds to `FishSim.resolution`, so this will not `throw`.
&#9;&#9;&#9;}
&#9;&#9;}
&#9;}

&#9;private void updateFish() {
&#9;&#9;updateFishLock.lock();
&#9;&#9;simUsages.startPhysics();

&#9;&#9;listToPartitions(grid, fishList);

&#9;&#9;// Update each fish
&#9;&#9;for(Fish fish : fishList) {
&#9;&#9;&#9;fish.applyFlockingRules(fishList, grid);
&#9;&#9;&#9;fish.update();
&#9;&#9;}

&#9;&#9;simUsages.postPhysics();
&#9;&#9;updateFishLock.unlock();
&#9;}

&#9;private void renderFish() {
&#9;&#9;renderFishLock.lock();
&#9;&#9;simUsages.startRender();
&#9;&#9;fishShown = 0;
//&#9;&#9;simUsages.preSynchro(); // Notice: alternatives: use `SDL_GL_SetSwapInterval(0);`, or move `simUsages.startRender()` below the first use of `SdlGles2`
&#9;&#9;gc.clearRect(0, 0, resolution[0], resolution[1]);
//&#9;&#9;simUsages.postSynchro();
&#9;&#9;for(Fish fish : fishList) {
&#9;&#9;&#9;if(fish.isVisible) { // For `Fish` not shown, this condition improves `SimUsages.fps` (lowers `SimUsages.renderNs`).
&#9;&#9;&#9;&#9;fishShown++;
&#9;&#9;&#9;&#9;fish.render(gc);
&#9;&#9;&#9;}
&#9;&#9;}
&#9;&#9;simUsages.postRender();
&#9;&#9;renderFishLock.unlock();
&#9;}

&#9;@Override
&#9;public void stop() {
&#9;&#9;executor.shutdown();
&#9;}

&#9;public class Fish { /* `static Fish` causes "{posBounds,posBounds.posBound()} cannot be referenced from a static context" (unless those are set to `static`, which prevents `FishSim` from use of separate values with multiple windows) */
&#9;&#9;public static Forces forcesSeparation = new Forces(22.0, 2.0);
&#9;&#9;public static Forces forcesSeparationNonsimilar = new Forces(100.0, 2.2);
&#9;&#9;public static Forces forcesAlignment = new Forces(100.0, 1.0);
&#9;&#9;public static Forces forcesCohesion = new Forces(100.0, 1.0);
&#9;&#9;public static Forces forcesBounds = new Forces(100.0, 2.0);
&#9;&#9;private static double dposMax = 3.0;   // Motion lim (limit of derivative of position)
&#9;&#9;private static double d2Pos = 0.1;     // Motion&lt;sup&gt;2&lt;/sup&gt; (derivative #2 of position)
&#9;&#9;private static double isSimilarTolerance = 0.2;
&#9;&#9;public static boolean applyWallAvoidanceTru = (PosBounds.PosBoundsMode.wrapAroundResolution != posBounds.getPosBoundsMode());
&#9;&#9;public static boolean redFishAreAggressiveOrPoisonous = true; // changes how `isSimilarTo(Fish other)` uses `color.getRed()`

&#9;&#9;private Pos pos;        // Position
&#9;&#9;private Pos dpos;       // Motion (derivative of position)
&#9;&#9;private Color color;
&#9;&#9;public boolean isInBounds;
&#9;&#9;public boolean isVisible = false; // Just stores `0 &lt;= pos[0] &amp;&amp; resolution[0] &gt; pos[0] &amp;&amp; 0 &lt;= pos[1]  &amp;&amp; resolution[1] &gt; pos[1]` for now.

&#9;&#9;public Fish(ImmutablePos pos, ImmutablePos dpos, Color color) {
&#9;&#9;&#9;this.pos = pos.clone(); // TODO: ensure this clones the actual (specialized) virtual function addresses, which improve CPU use
&#9;&#9;&#9;this.dpos = dpos.clone();
&#9;&#9;&#9;this.color = color;
&#9;&#9;}
&#9;&#9;public Fish(Pos pos, Pos dpos, Color color) { /* Notice: uses "placement moves" for {`pos`, `dpos`}. Gives `Fish` ownership of `pos`, ownership of `dpos`. */
&#9;&#9;&#9;this.pos = pos;
&#9;&#9;&#9;this.dpos = dpos;
&#9;&#9;&#9;this.color = color;
&#9;&#9;}

&#9;&#9;public boolean isSimilarTo(Fish o) {
//&#9;&#9;&#9;return color.equals(o.color); // Less CPU use, but requires that `fishList` has just a few colors.
//&#9;&#9;&#9;Color colorDis = Color.color(color.getRed() - o.color.getRed(), color.getGreen() - o.color.getGreen(), color.getBlue() - o.color.getBlue()); // Notice: `Color` is more intuitive to use, but was concerned that `java` will not fold this
&#9;&#9;&#9;double[] colorDis = {color.getRed() - o.color.getRed(), color.getGreen() - o.color.getGreen(), color.getBlue() - o.color.getBlue()};
&#9;&#9;&#9;if(redFishAreAggressiveOrPoisonous) {
&#9;&#9;&#9;&#9;colorDis[0] = Calculus.pow2(colorDis[0]);
&#9;&#9;&#9;}
//&#9;&#9;&#9;return isSimilarTolerance &gt; (Math.hypot(Math.abs(colorDis[0]), Math.abs(colorDis[1]), Math.abs(colorDis[2]))); // [`Math.hypot()` still does not support &gt; 2 dimensions?](https://esdiscuss.org/topic/how-about-more-args-for-math-hypot). Notice: if you use Euclidean distance, lower `isSimilarTolerance`.
&#9;&#9;&#9;return isSimilarTolerance &gt; (Calculus.pow2(colorDis[0]) + Calculus.pow2(colorDis[1]) + Calculus.pow2(colorDis[2]));
&#9;&#9;} // TODO: Use a function (such as `javafx.scene.shape.Polygon.getPoints()`) for comparison of vertices. */

&#9;&#9;public Pos getPosDiff(Fish o) {
&#9;&#9;&#9;return posBounds.posDiff(pos, o.pos);
&#9;&#9;}

&#9;&#9;public synchronized void setPos(Pos newPos /* Notice: semantics of "placement move" */) {
&#9;&#9;&#9;isInBounds = posBounds.posBound(newPos); // If `PosBoundsMode.boundless != posBounds.getPosBoundsMode()`, this ensures the invariant `0 &lt;= pos[dim] &amp;&amp; PosBounds.getBounds()[dim] &gt; pos[dim]` is established.
&#9;&#9;&#9;isVisible = (0 &lt;= newPos.pos[0] &amp;&amp; resolution[0] &gt; newPos.pos[0] &amp;&amp; 0 &lt;= newPos.pos[1]  &amp;&amp; resolution[1] &gt; newPos.pos[1]); // TODO: +`Pos::isLessOrEquals(Pos)`, +`Pos::isMore(pos)`
&#9;&#9;&#9;if((!isInBounds) &amp;&amp; PosBounds.PosBoundsMode.boundless != posBounds.getPosBoundsMode()) {
&#9;&#9;&#9;&#9;outOfBounds("Fish::setPos", this);
&#9;&#9;&#9;}
&#9;&#9;&#9;pos = newPos; /* Notice: does not use `newPos.clone()` since this function is used in inner loops. After this function returns, `this` has ownership of `newPos`. */
&#9;&#9;}
&#9;&#9;public synchronized void setPos(ImmutablePos newPos) {
&#9;&#9;&#9;setPos(newPos.clone()); /* Notice: since this function is used in inner loops, `setPos(Pos newPos)` uses the semantics of "placement move", so if `newPos` is immutable, must clone. */
&#9;&#9;}

&#9;&#9;public synchronized void applyFlockingRules(List&lt;Fish&gt; allFish, List&lt;Fish&gt;[][] grid) {
&#9;&#9;&#9;int[] gridPos = {(int) (pos.pos[0] / gridResolution), (int) (pos.pos[1] / gridResolution)};
&#9;&#9;&#9;List&lt;Fish&gt; nearbyFish = new ArrayList&lt;&gt;();

&#9;&#9;&#9;// Check neighboring grid cells
&#9;&#9;&#9;if(PosBounds.PosBoundsMode.wrapAroundResolution == posBounds.getPosBoundsMode()) {
&#9;&#9;&#9;&#9;for(int i = gridPos[0] - 1; i &lt;= gridPos[0] + 1; i++) {
&#9;&#9;&#9;&#9;&#9;for(int j = gridPos[1] - 1; j &lt;= gridPos[1] + 1; j++) {
&#9;&#9;&#9;&#9;&#9;&#9;nearbyFish.addAll(grid[(i + grid.length) % grid.length][(j + grid[0].length) % grid[0].length]);
&#9;&#9;&#9;&#9;&#9;} /* TODO: move expensive `%`s into outer loop somehow. With `1000 == fishList.size() &amp;&amp; 100 == monitorRefreshHertz`, 2 `%`s in inner loop executes 200,000 `%`/s. The most simple solution is to use outer branches to choose from loops which hardcode this, but thus duplicates codeflow. Does `java` do this for you if the loop uses most of the CPU? */
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;} else {
&#9;&#9;&#9;&#9;for(int i = Math.max(0, gridPos[0] - 1); i &lt;= Math.min(grid.length - 1, gridPos[0] + 1); i++) {
&#9;&#9;&#9;&#9;&#9;for(int j = Math.max(0, gridPos[1] - 1); j &lt;= Math.min(grid[0].length - 1, gridPos[1] + 1); j++) {
&#9;&#9;&#9;&#9;&#9;&#9;nearbyFish.addAll(grid[i][j]);
&#9;&#9;&#9;&#9;&#9;} /* TODO: move expensive `Math.{min,max}`s into outer loop somehow. With `1000 == fishList.size() &amp;&amp; 100 == monitorRefreshHertz`, inner loop executes 200,000 `Math.{min,max}`/s */
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;}

&#9;&#9;&#9;applySeparation(nearbyFish);
&#9;&#9;&#9;applyAlignment(nearbyFish);
&#9;&#9;&#9;applyCohesion(nearbyFish);
&#9;&#9;&#9;if(applyWallAvoidanceTru) {
&#9;&#9;&#9;&#9;applyWallAvoidance(posBounds.getBounds());
&#9;&#9;&#9;}
&#9;&#9;}

&#9;&#9;private synchronized void applySeparation(List&lt;Fish&gt; nearbyFish) {
&#9;&#9;&#9;Pos sepDpos = dpos.zeros();
&#9;&#9;&#9;Pos sepNonsimilarDpos = dpos.zeros();
&#9;&#9;&#9;int count = 0, countNonsimilar = 0;

&#9;&#9;&#9;for(Fish o : nearbyFish) {
&#9;&#9;&#9;&#9;if(o != this) {
&#9;&#9;&#9;&#9;&#9;Pos posDiff = getPosDiff(o);
&#9;&#9;&#9;&#9;&#9;double dist = posDiff.magnitude(); // Notice: in future, +`Pos::boundHypotenus(o)`
&#9;&#9;&#9;&#9;&#9;if(isSimilarTo(o)) {
&#9;&#9;&#9;&#9;&#9;&#9;if(forcesSeparation.posIfDistScaleSum(sepDpos, posDiff, dist)) {
&#9;&#9;&#9;&#9;&#9;&#9;&#9;count++;
&#9;&#9;&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;&#9;&#9;} else {
&#9;&#9;&#9;&#9;&#9;&#9;if(forcesSeparationNonsimilar.posIfDistScaleSum(sepNonsimilarDpos, posDiff, dist)) {
&#9;&#9;&#9;&#9;&#9;&#9;&#9;countNonsimilar++;
&#9;&#9;&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;}

&#9;&#9;&#9;if(count &gt; 0) {
&#9;&#9;&#9;&#9;sepDpos.slashEqualsScalar(count);
&#9;&#9;&#9;&#9;forcesSeparation.dposScaleSum(dpos, d2Pos, sepDpos);
&#9;&#9;&#9;}
&#9;&#9;&#9;if(countNonsimilar &gt; 0) {
&#9;&#9;&#9;&#9;sepNonsimilarDpos.slashEqualsScalar(countNonsimilar);
&#9;&#9;&#9;&#9;forcesSeparationNonsimilar.dposScaleSum(dpos, d2Pos, sepNonsimilarDpos);
&#9;&#9;&#9;}
&#9;&#9;}

&#9;&#9;private synchronized void applyAlignment(List&lt;Fish&gt; nearbyFish) {
&#9;&#9;&#9;Pos avgDpos = dpos.zeros();
&#9;&#9;&#9;int count = 0;

&#9;&#9;&#9;for(Fish o : nearbyFish) {
&#9;&#9;&#9;&#9;if(o != this &amp;&amp; isSimilarTo(o)) {
&#9;&#9;&#9;&#9;&#9;Pos posDiff = getPosDiff(o);
&#9;&#9;&#9;&#9;&#9;double distPow2 = posDiff.magnitudePow2();
&#9;&#9;&#9;&#9;&#9;if(forcesAlignment.posIfDistPow2Sum(avgDpos, o.dpos, distPow2)) {
&#9;&#9;&#9;&#9;&#9;&#9;count++;
&#9;&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;}

&#9;&#9;&#9;if(count &gt; 0) {
&#9;&#9;&#9;&#9;avgDpos.slashEqualsScalar(count);
&#9;&#9;&#9;&#9;forcesAlignment.dposScaleSum(dpos, d2Pos, avgDpos);
&#9;&#9;&#9;}
&#9;&#9;}

&#9;&#9;private synchronized void applyCohesion(List&lt;Fish&gt; nearbyFish) {
&#9;&#9;&#9;Pos avgPos = pos.zeros();
&#9;&#9;&#9;int count = 0;

&#9;&#9;&#9;for(Fish o : nearbyFish) {
&#9;&#9;&#9;&#9;if(o != this &amp;&amp; isSimilarTo(o)) {
&#9;&#9;&#9;&#9;&#9;Pos posDiff = getPosDiff(o);
&#9;&#9;&#9;&#9;&#9;double distPow2 = posDiff.magnitudePow2();
&#9;&#9;&#9;&#9;&#9;if(forcesCohesion.posIfDistPow2Sum(avgPos, o.pos, distPow2)) {
&#9;&#9;&#9;&#9;&#9;&#9;count++;
&#9;&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;}

&#9;&#9;&#9;if(count &gt; 0) {
&#9;&#9;&#9;&#9;avgPos.slashEqualsScalar(count);
&#9;&#9;&#9;&#9;forcesCohesion.dposScaleSum(dpos, d2Pos, posBounds.posDiff(avgPos, pos));
&#9;&#9;&#9;}
&#9;&#9;}

&#9;&#9;private synchronized void applyWallAvoidance(double[] res) {
&#9;&#9;&#9;Pos avoidancePos = dpos.zeros();

&#9;&#9;&#9;if(pos.pos[0] &lt; forcesBounds.distance) {
&#9;&#9;&#9;&#9;avoidancePos.pos[0] += (forcesBounds.distance - pos.pos[0]);
&#9;&#9;&#9;} else if(pos.pos[0] &gt; res[0] - forcesBounds.distance) {
&#9;&#9;&#9;&#9;avoidancePos.pos[0] -= (pos.pos[0] - (res[0] - forcesBounds.distance));
&#9;&#9;&#9;}
&#9;&#9;&#9;if(pos.pos[1] &lt; forcesBounds.distance) {
&#9;&#9;&#9;&#9;avoidancePos.pos[1] += (forcesBounds.distance - pos.pos[1]);
&#9;&#9;&#9;} else if(pos.pos[1] &gt; res[1] - forcesBounds.distance) {
&#9;&#9;&#9;&#9;avoidancePos.pos[1] -= (pos.pos[1] - (res[1] - forcesBounds.distance));
&#9;&#9;&#9;}

&#9;&#9;&#9;avoidancePos.starEqualsScalar(d2Pos * forcesBounds.factor / forcesBounds.distance);
&#9;&#9;&#9;dpos.plusEquals(avoidancePos);
&#9;&#9;}

&#9;&#9;public synchronized void update() {
&#9;&#9;&#9;// Limit speed
&#9;&#9;&#9;double speed = dpos.magnitude();
&#9;&#9;&#9;if(speed &gt; dposMax) {
&#9;&#9;&#9;&#9;dpos.slashEqualsScalar(speed);
&#9;&#9;&#9;&#9;dpos.starEqualsScalar(dposMax);
&#9;&#9;&#9;}

&#9;&#9;&#9;// Update position
&#9;&#9;&#9;setPos(pos.plus(dpos));
&#9;&#9;}

&#9;&#9;public synchronized void render(GraphicsContext gc) {
&#9;&#9;&#9;gc.save();
&#9;&#9;&#9;gc.translate(pos.pos[0], pos.pos[1]);
&#9;&#9;&#9;gc.rotate(Math.toDegrees(Math.atan2(dpos.pos[1], dpos.pos[0])) + 90);
&#9;&#9;&#9;gc.setFill(color);
&#9;&#9;&#9;gc.beginPath();
&#9;&#9;&#9;gc.moveTo(0, -10);
&#9;&#9;&#9;gc.lineTo(-5, 10);
&#9;&#9;&#9;gc.lineTo(-2, 0);
&#9;&#9;&#9;gc.lineTo(2, 0);
&#9;&#9;&#9;gc.lineTo(5, 10);
&#9;&#9;&#9;gc.closePath();
&#9;&#9;&#9;gc.fill();
&#9;&#9;&#9;gc.restore();
&#9;&#9;}
&#9;};
};</code></pre></div>]]></content:encoded></item><item><title><![CDATA[[Preview] TensorFlow produces meshes]]></title><description><![CDATA[{description, mesh} tuples show tensorflow how to use new descriptions to produce new meshes]]></description><link>https://swudususuwu.substack.com/p/tensorflow-produces-meshes</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/tensorflow-produces-meshes</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Wed, 07 Jan 2026 21:30:08 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/30e627c0-c265-4f85-b6c3-0aa07b9232de_1430x760.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>[<a href="https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/Tensorflow_meshes.md">This post</a> is released through <em><a href="https://creativecommons.org/licenses/by/2.0/">Creative Commons Attribution 2</a></em>. Unfinished: version is <a href="https://github.com/SwuduSusuwu/SusuPosts/commit/832f16fa2a880fcd9c9fbf7904fb5ad75627bcf7">832f16f</a>]</p><p><a href="https://github.com/tensorflow/tensorflow">tensorflow</a> loads examples of <code>{input, output}</code> (which is <code>{question, answer}</code>) to produce connectomes (groups of synapses) which process future questions into new answers.</p><div><hr></div><h1>What are meshes</h1><p>This document uses <a href="https://swudususuwu.substack.com/i/176876856/popular-structures-for-sims">./Physics_sims_which_structures_to_use.md#popular-structures-for-sims</a>'s definitions of <em>renderers</em> + <em>descriptions</em> + <em>textures</em> + <em>meshes</em>.</p><p>Thus, the purpose of this document is to assist <em>renderer</em> usage (allow to give simple descriptions to <code>tensorflow</code>, which then produces new <em>meshes</em>).</p><p>The formulas (in the discussions which follow) use <code>description</code>s to produce <code>mesh</code>s. Such formulas are also suitable to use <code>description</code>s to produce <code>texture</code>s.</p><div><hr></div><h1>Discussion</h1><p>From <a href="https://poe.com/s/ikGB3CcLax9Tmla33unB">this discusssion with </a><em><a href="https://poe.com/s/ikGB3CcLax9Tmla33unB">Solar-Pro-2</a></em>:</p><p><strong>Q</strong>: &#8220;Wish you produce source code which processes tuples of <code>{text description, mesh}</code> through <code>tensorflow</code> to convert new descriptions into meshes (for procedural sims).</p><p>The goal is:</p><ul><li><p>Download all public stores of meshes (numerous platforms such as Godot-4 have public stores of meshes which allow unrestricted uses). You can assume local scripts do this</p></li><li><p>Send the descriptions of those meshes (plus those meshes) into this <code>PyTorch</code> or <code>tensorflow</code> tool. For meshes without descriptions, guess will use just the filenames (or use a simple mesh classification tool to produce descriptions for thus), but most have descriptions, so you can just assume that all meshes have descriptions to use.</p></li><li><p>Use this (with new descriptions) as a procedural tool to produce new meshes.</p></li></ul><p>Is more natural to map text to textures+mesh (plus, most relevent formats store textures+mesh), but this is just a proof-of-concept, so assume a simple mesh format (which just consists of edges+vertices).</p><p>If a serious difficult step is to process the text into info which <code>tensorflow</code> can use, you can use &#8220;transfer learner&#8221; preprocessor steps which leverage current Natural Language Processors to reduce the text into info which is more simple-to-use, but guess that <code>tensorflow</code> will do so on its own if given sufficient inputs of <code>{description, mesh}</code> tuples.&#8221;</p><h2><em>Solar-Pro-2</em>&#8216;s <code>tensorflow</code> solution</h2><p>Notice: the <em><a href="https://poe.com/s/ikGB3CcLax9Tmla33unB">block quote</a></em><a href="https://poe.com/s/ikGB3CcLax9Tmla33unB"> which follows</a> was produced from the <em><a href="https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/ConsumerWarnings/Warning_AI_is_not_good_for_humans_to_use.md">artificial intelligence</a></em> known as <em><a href="https://poe.com/Solar-Pro-2">Solar-Pro-2</a></em>.</p><blockquote><p>Creating a system that maps text descriptions to 3D meshes is an ambitious but feasible proof-of-concept project. Below is a comprehensive approach using TensorFlow, with notes on how to handle the text-to-mesh translation pipeline. Since this is a complex task, I&#8217;ll outline the architecture and provide sample code for key components.</p><div><hr></div><h3><strong>1. Data Collection &amp; Preprocessing</strong></h3><p>First, we need to gather <code>{description, mesh}</code> pairs. For this proof-of-concept, we&#8217;ll assume:</p><ul><li><p>Meshes are in a simple format (vertices + edges, e.g., <code>.obj</code> with just <code>v</code> and <code>l</code> lines).</p></li><li><p>Descriptions are available (from metadata or filenames).</p></li></ul><h4><strong>Mesh Simplification (Optional)</strong></h4><p>If working with complex meshes, simplify them (e.g., using <code>pyMeshLab</code> or <code>open3d</code>).</p><h4><strong>Text Preprocessing</strong></h4><p>Use a transformer-based model (e.g., <code>BERT</code>, <code>CLIP</code>, or <code>T5</code>) to encode descriptions into embeddings.</p><h4><strong>Mesh Representation</strong></h4><p>Convert meshes into a consistent format:</p><ul><li><p><strong>Vertices</strong>: <code>(N, 3)</code> coordinates (x, y, z).</p></li><li><p><strong>Edges</strong>: <code>(M, 2)</code> indices connecting vertices.</p></li><li><p><strong>Normalization</strong>: Scale meshes to fit in a unit cube.</p></li></ul><div><hr></div><h3><strong>2. Model Architecture</strong></h3><p>We&#8217;ll use a <strong>Transformer-based encoder-decoder</strong> model:</p><ul><li><p><strong>Text Encoder</strong>: Pre-trained CLIP or BERT to extract text embeddings.</p></li><li><p><strong>Mesh Decoder</strong>: Transformer or graph neural network (GNN) to generate mesh vertices/edges from text embeddings.</p></li></ul><h4><strong>Alternative Approach (Diffusion Models)</strong></h4><p>For more realistic results, consider using a diffusion model (e.g., <code>Score-SDE</code>) to generate meshes from text embeddings.</p><div><hr></div><h3><strong>3. Code Implementation (TensorFlow + CLIP)</strong></h3><p>Below is a simplified implementation using TensorFlow and CLIP for text encoding.</p><pre><code>import tensorflow as tf
import numpy as np
import clip
import os
from sklearn.preprocessing import normalize
from tensorflow.keras import layers, Model

# 1. Load CLIP for text encoding
device = &#8220;cuda:0&#8221; if tf.test.is_gpu_available() else &#8220;cpu&#8221;
clip_model, preprocess = clip.load(&#8221;ViT-B/32&#8221;, device=device)
clip_model.eval()

def encode_text(description):
    with tf.device(&#8221;/device:CPU:0&#8221; if device != &#8220;cuda:0&#8221; else &#8220;/device:GPU:0&#8221;):
        text = clip.tokenize([description]).to(device)
        with torch.no_grad():
            text_emb = clip_model.encode_text(text).cpu().numpy()
        return normalize(text_emb[0])

# 2. Mesh Dataset Loader
class MeshDataset(tf.keras.utils.Sequence):
    def __init__(self, data_dir, batch_size=32):
        self.data_dir = data_dir
        self.batch_size = batch_size
        self.filenames = [f for f in os.listdir(data_dir) if f.endswith(&#8221;.obj&#8221;)]
        self.text_descriptions = [os.path.splitext(f)[0].replace(&#8221;_&#8221;, &#8220; &#8220;).title() for f in self.filenames]

    def __len__(self):
        return len(self.filenames) // self.batch_size

    def load_mesh(self, filename):
        vertices = []
        edges = []
        with open(os.path.join(self.data_dir, filename), &#8220;r&#8221;) as f:
            for line in f:
                if line.startswith(&#8221;v &#8220;):
                    vertices.append(np.array([float(x) for x in line.split()[1:4]]))
                elif line.startswith(&#8221;l &#8220;):
                    edges.append([int(x)-1 for x in line.split()[1:3]])
        return np.array(vertices), np.array(edges)

    def __getitem__(self, idx):
        batch_files = self.filenames[idx*self.batch_size:(idx+1)*self.batch_size]
        batch_texts = self.text_descriptions[idx*self.batch_size:(idx+1)*self.batch_size]

        text_embeddings = np.stack([encode_text(txt) for txt in batch_texts])

        meshes = [self.load_mesh(f) for f in batch_files]
        max_vertices = max([len(v) for v, _ in meshes])
        max_edges = max([len(e) for _, e in meshes])

        vertices_batch = np.zeros((self.batch_size, max_vertices, 3))
        edges_batch = np.zeros((self.batch_size, max_edges, 2))
        lengths = []

        for i, (v, e) in enumerate(meshes):
            vertices_batch[i, :len(v)] = v
            edges_batch[i, :len(e)] = e
            lengths.append((len(v), len(e)))

        return text_embeddings, (vertices_batch, edges_batch, lengths)

# 3. Model Architecture (Transformer-based Mesh Generator)
def build_mesh_generator(text_input_dim=512, max_vertices=1024, max_edges=2048):
    # Text Encoder (using CLIP embeddings directly as input)
    text_input = layers.Input(shape=(text_input_dim,), name=&#8221;text_input&#8221;)

    # Transformer Encoder for Text
    transformer_encoder = layers.MultiHeadAttention(
        num_heads=8, key_dim=text_input_dim // 8
    )
    transformer_encoder_out = transformer_encoder(text_input, text_input)

    # Mesh Decoder (Vertex Generator)
    vertex_output = layers.Dense(max_vertices * 3, activation=&#8221;tanh&#8221;)(transformer_encoder_out)
    vertex_output = layers.Reshape((max_vertices, 3))(vertex_output)

    # Edge Generator (Simple Approach: Predict Adjacency Matrix)
    edge_logits = layers.Dense(max_vertices * max_vertices, activation=&#8221;sigmoid&#8221;)(transformer_encoder_out)
    edge_logits = layers.Reshape((max_vertices, max_vertices))(edge_logits)

    # Create model
    model = Model(inputs=text_input, outputs=[vertex_output, edge_logits])

    # Compile with custom loss (handle variable-length sequences)
    def custom_loss(y_true, y_pred):
        vertex_true, edge_true, lengths = y_true
        vertex_pred, edge_pred = y_pred

        # Vertex loss (only compute for existing vertices)
        vertex_mask = tf.sequence_mask(lengths[0][0], max_vertices)
        vertex_loss = tf.reduce_mean(tf.square(vertex_true * vertex_mask[:, :, tf.newaxis] - vertex_pred * vertex_mask[:, :, tf.newaxis]))

        # Edge loss (binary cross-entropy for existing edges)
        edge_mask = tf.sequence_mask(lengths[0][1], max_edges)
        edge_true_dense = tf.SparseTensor(
            indices=tf.stack([tf.range(tf.shape(edge_true)[0]), tf.range(tf.shape(edge_true)[1])], axis=1),
            values=tf.ones_like(edge_true[:, 0]),
            dense_shape=tf.shape(edge_true)
        )
        edge_loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=edge_true, logits=edge_pred))

        return vertex_loss + edge_loss

    model.compile(optimizer=&#8221;adam&#8221;, loss=custom_loss)
    return model

# 4. Training Pipeline
def train_model(data_dir, epochs=10, batch_size=16):
    dataset = MeshDataset(data_dir, batch_size=batch_size)
    text_input_dim = 512  # CLIP embedding size

    model = build_mesh_generator(text_input_dim=text_input_dim)

    # Training loop (since dataset is a custom Sequence)
    for epoch in range(epochs):
        print(f&#8221;\nEpoch {epoch+1}/{epochs}&#8221;)
        progbar = tf.keras.utils.Progbar(len(dataset))

        for step, (text_embeddings, mesh_data) in enumerate(dataset):
            vertices_batch, edges_batch, lengths = mesh_data

            # Convert edges to dense adjacency matrix
            batch_size = tf.shape(edges_batch)[0]
            max_edges = tf.shape(edges_batch)[1]
            edge_masks = tf.SparseTensor(
                indices=tf.stack([
                    tf.range(batch_size),
                    tf.range(max_edges)
                ], axis=1),
                values=tf.ones((batch_size, max_edges), dtype=tf.float32),
                dense_shape=(batch_size, max_edges)
            )

            edge_targets = tf.SparseTensor(
                indices=tf.reshape(edges_batch, (-1, 2)),
                values=tf.ones(tf.shape(edges_batch)[0] * tf.shape(edges_batch)[1], dtype=tf.float32),
                dense_shape=(batch_size, max_vertices, max_vertices)
            )

            loss = model.train_on_batch(text_embeddings, [vertices_batch, edge_targets])
            progbar.add(1, values=[(&#8221;loss&#8221;, loss)])

    return model

# 5. Inference Function
def generate_mesh_from_text(model, description, max_vertices=1024):
    text_embedding = encode_text(description)
    vertex_logits, edge_logits = model.predict(text_embedding[np.newaxis, ...])

    # Post-processing: Threshold edges
    edges = tf.where(edge_logits[0] &gt; 0.5)  # Threshold for edge existence
    edges = tf.gather(edges, 0, axis=1)    # Keep only the edge indices

    # Filter out duplicate edges (A-B and B-A are the same)
    unique_edges = []
    seen = set()
    for edge in edges:
        sorted_edge = tuple(sorted(edge.numpy()))
        if sorted_edge not in seen:
            seen.add(sorted_edge)
            unique_edges.append(edge.numpy())
    unique_edges = np.array(unique_edges)

    # Extract valid vertices (those referenced by edges)
    referenced_vertices = set()
    for edge in unique_edges:
        referenced_vertices.update(edge)
    valid_vertices = vertex_logits[0][list(referenced_vertices)]

# Write to .obj file
def save_mesh_to_obj(vertices, edges, filename=&#8221;output.obj&#8221;):
    with open(filename, &#8220;w&#8221;) as f:
        # Write vertices
        for v in vertices:
            f.write(f&#8221;v {&#8217; &#8216;.join(map(str, v))}\n&#8221;)
        # Write edges (lines)
        for e in edges:
            f.write(f&#8221;l {int(e[0])+1} {int(e[1])+1}\n&#8221;)  # OBJ is 1-indexed

save_mesh_to_obj(valid_vertices, unique_edges)

# Example usage:
# model = train_model(&#8221;path/to/mesh_data&#8221;)
# generate_mesh_from_text(model, &#8220;a simple cube with rounded corners&#8221;)</code></pre><div><hr></div><h3><strong>Key Improvements &amp; Notes</strong></h3><ul><li><p><strong>Mesh Validation</strong>: The current edge generation produces a dense adjacency matrix, which is inefficient. For production, consider:</p><ul><li><p>Using sparse tensor operations directly</p></li><li><p>Implementing a graph neural network (GNN) for better topology prediction</p></li><li><p>Adding manifold checks (e.g., via <code>open3d</code>)</p></li></ul></li><li><p><strong>Text Encoding</strong>: For better results, use:</p></li></ul><pre><code>import torch
text_embedding = clip_model.encode_text(clip.tokenize([description]).to(device)).cpu().numpy()</code></pre><ul><li><p><strong>Loss Function</strong>: The current loss is simplistic. Consider:</p><ul><li><p>Chamfer distance for vertex alignment</p></li><li><p>Edge accuracy metrics</p></li><li><p>Topology-aware losses (e.g., Euler characteristic)</p></li></ul></li><li><p><strong>Diffusion Alternative</strong>: For higher-quality results, explore diffusion models like:</p></li></ul><pre><code># (Pseudo-code for diffusion approach)
noise_scheduler = DDPMScheduler.from_pretrained(&#8221;stability-ai/sdxl-turbo&#8221;)
unet = UNet2DConditionModel.from_pretrained(&#8221;stability-ai/sdxl-turbo&#8221;)</code></pre><pre><code><code>
Would you like a version using PyTorch + Diffusion for better mesh generation quality? Let me know if you need help with any specific part!
</code></code></pre></blockquote><div><hr></div><h1>Discussion 2</h1><p>From <a href="https://poe.com/s/WbJpmsN4zNAOtdoEgSSa">this discusssion with </a><em><a href="https://poe.com/s/WbJpmsN4zNAOtdoEgSSa">Grok-2</a></em></p><p><strong>Q</strong>: &#8220;Wish you produce <code>tensorflow_meshes.java</code> which processes tuples of {text description, mesh} through tensorflow to convert new descriptions into meshes (for procedural sims).</p><p>The goal is:</p><ul><li><p>Download all public stores of meshes (numerous platforms such as Godot-4 have public stores of meshes which allow unrestricted uses). You can assume local scripts downloaded those (loaded into <code>class StrMesh {String description, Mesh mesh};</code>)</p></li><li><p>Send the descriptions of those meshes (plus those meshes) into this <code>tensorflow</code> tool.</p></li><li><p>Use this (with new descriptions) as a procedural tool which produces new meshes.</p></li></ul><p>Is more natural to map text to textures+mesh (plus, most relevent formats store textures+mesh), but this is just a proof-of-concept, so assume a simple <code>class Mesh</code> (which just consists of edges plus vertices).</p><p>If a serious difficult step is to process the text into info which <code>tensorflow</code> can use, you can use &#8220;transfer learner&#8221; preprocessor steps (which leverage current <em>Natural Language Processors</em> to reduce the text into info which is more simple-to-use), but if <code>tensorflow</code> is setup with sufficient representative <code>class StrMesh</code>s (plus sufficient connectome room), <code>tensorflow</code> should know how to do process new <code>String description</code>s into <code>Mesh mesh</code>s on its own&#8221;</p><h2><em>Grok-2</em>&#8216;s <code>tensorflow</code> solution</h2><p>Notice: the <em>block quote</em> which follows was produced from the <em>artificial intelligence</em> known as <em><a href="https://poe.com/Grok-2">Grok-2</a></em>.</p><blockquote><p>Let&#8217;s approach this step-by-step to create a Java class that uses TensorFlow to process text descriptions and generate meshes. We&#8217;ll assume that we have a local collection of <code>StrMesh</code> objects and that TensorFlow is set up with the necessary libraries.</p><p>Here&#8217;s a plan for the <code>tensorflow_meshes.java</code> file:</p><ol><li><p>Import necessary libraries</p></li><li><p>Define the <code>StrMesh</code> class</p></li><li><p>Define the <code>Mesh</code> class</p></li><li><p>Create a <code>TensorFlowMeshes</code> class with methods for:</p><ul><li><p>Loading and preprocessing data</p></li><li><p>Building and training the model</p></li><li><p>Generating new meshes from text descriptions</p></li></ul></li><li><p>Implement a main method to demonstrate the usage</p></li></ol><p>Let&#8217;s implement this plan:</p><pre><code>import org.tensorflow.*;
import org.tensorflow.op.Ops;
import org.tensorflow.op.core.Placeholder;
import org.tensorflow.op.math.MatMul;
import org.tensorflow.types.TFloat32;
import org.tensorflow.types.TString;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class tensorflow_meshes {

    // Define the StrMesh class
    public static class StrMesh {
        String description;
        Mesh mesh;

        public StrMesh(String description, Mesh mesh) {
            this.description = description;
            this.mesh = mesh;
        }
    }

    // Define the Mesh class
    public static class Mesh {
        List&lt;float[]&gt; vertices;
        List&lt;int[]&gt; edges;

        public Mesh(List&lt;float[]&gt; vertices, List&lt;int[]&gt; edges) {
            this.vertices = vertices;
            this.edges = edges;
        }
    }

    public static class TensorFlowMeshes {
        private List&lt;StrMesh&gt; trainingData;
        private SavedModelBundle model;

        public TensorFlowMeshes(List&lt;StrMesh&gt; trainingData) {
            this.trainingData = trainingData;
        }

        // Method to preprocess the data
        private List&lt;StrMesh&gt; preprocessData() {
            // In a real implementation, we would use NLP techniques here
            // For this proof of concept, we&#8217;ll just return the original data
            return trainingData;
        }

        // Method to build and train the model
        public void buildAndTrainModel() {
            try (Graph graph = new Graph()) {
                Ops tf = Ops.create(graph);

                // Input placeholders
                Placeholder&lt;TString&gt; descriptionInput = tf.placeholder(TString.class, Placeholder.shape(Shape.unknown()));
                Placeholder&lt;TFloat32&gt; meshInput = tf.placeholder(TFloat32.class, Placeholder.shape(Shape.unknown()));

                // Simple model (in reality, this would be much more complex)
                MatMul output = tf.math.matMul(descriptionInput, meshInput);

                // In a real implementation, we would add training steps here
                // For this proof of concept, we&#8217;ll just save the model

                this.model = SavedModelBundle.load(&#8221;tensorflow_meshes_model&#8221;, &#8220;serve&#8221;);
            }
        }

        // Method to generate a new mesh from a text description
        public Mesh generateMesh(String description) {
            if (model == null) {
                throw new IllegalStateException(&#8221;Model has not been trained and saved yet.&#8221;);
            }

            try (Session session = model.session()) {
                Tensor&lt;TString&gt; inputTensor = TString.scalarOf(description);

                // Run the model
                Tensor&lt;?&gt; outputTensor = session.runner()
                        .feed(&#8221;input&#8221;, inputTensor)
                        .fetch(&#8221;output&#8221;)
                        .run()
                        .get(0);

                // Convert the output tensor to a Mesh object
                // This is a simplified version; in reality, this would be more complex
                float[] outputData = new float[outputTensor.numElements()];
                outputTensor.copyTo(outputData);

                List&lt;float[]&gt; vertices = new ArrayList&lt;&gt;();
                List&lt;int[]&gt; edges = new ArrayList&lt;&gt;();

                // Assume the output is a flattened representation of vertices and edges
                int vertexCount = (int) outputData[0];
                int edgeCount = (int) outputData[vertexCount * 3 + 1];

                for (int i = 0; i &lt; vertexCount; i++) {
                    float[] vertex = new float[3];
                    System.arraycopy(outputData, i * 3 + 1, vertex, 0, 3);
                    vertices.add(vertex);
                }

                for (int i = 0; i &lt; edgeCount; i++) {
                    int[] edge = new int[2];
                    edge[0] = (int) outputData[vertexCount * 3 + i * 2 + 2];
                    edge[1] = (int) outputData[vertexCount * 3 + i * 2 + 3];
                    edges.add(edge);
                }

                return new Mesh(vertices, edges);
            }
        }
    }

    public static void main(String[] args) {
        // Create sample training data
        List&lt;StrMesh&gt; trainingData = new ArrayList&lt;&gt;();
        Random random = new Random();

        for (int i = 0; i &lt; 100; i++) {
            String description = &#8220;Sample description &#8220; + i;
            List&lt;float[]&gt; vertices = new ArrayList&lt;&gt;();
            List&lt;int[]&gt; edges = new ArrayList&lt;&gt;();

            for (int j = 0; j &lt; 10; j++) {
                vertices.add(new float[]{random.nextFloat(), random.nextFloat(), random.nextFloat()});
            }

            for (int j = 0; j &lt; 15; j++) {
                edges.add(new int[]{random.nextInt(10), random.nextInt(10)});
            }

            trainingData.add(new StrMesh(description, new Mesh(vertices, edges)));
        }

        // Create and train the model
        TensorFlowMeshes tfMeshes = new TensorFlowMeshes(trainingData);
        tfMeshes.buildAndTrainModel();

        // Generate a new mesh
        String newDescription = &#8220;A new, unique mesh&#8221;;
        Mesh newMesh = tfMeshes.generateMesh(newDescription);

        // Print the result
        System.out.println(&#8221;Generated mesh for description: &#8220; + newDescription);
        System.out.println(&#8221;Vertices: &#8220; + newMesh.vertices.size());
        System.out.println(&#8221;Edges: &#8220; + newMesh.edges.size());
    }
}</code></pre><p>This implementation provides a basic structure for processing text descriptions and generating meshes using TensorFlow. Here are some key points about this implementation:</p><ol><li><p>We&#8217;ve defined the <code>StrMesh</code> and <code>Mesh</code> classes as specified.</p></li><li><p>The <code>TensorFlowMeshes</code> class handles the main functionality:</p><ul><li><p>It takes a list of <code>StrMesh</code> objects as training data.</p></li><li><p>The <code>preprocessData</code> method is a placeholder for where you would implement NLP techniques to process the text descriptions.</p></li><li><p>The <code>buildAndTrainModel</code> method creates a very simple model (in reality, this would be much more complex) and saves it.</p></li><li><p>The <code>generateMesh</code> method uses the trained model to generate a new mesh from a given description.</p></li></ul></li><li><p>The <code>main</code> method demonstrates how to use the class with sample data.</p></li></ol><p>Please note that this is a highly simplified proof of concept. In a real-world implementation:</p><ul><li><p>You would need to implement more sophisticated NLP techniques in the <code>preprocessData</code> method.</p></li><li><p>The model architecture in <code>buildAndTrainModel</code> would need to be much more complex to handle the task of generating meshes from text.</p></li><li><p>The <code>generateMesh</code> method would need to be adapted to handle the actual output of your model.</p></li><li><p>You would need to handle loading of real data from the public stores you mentioned.</p></li><li><p>Error handling and input validation would be crucial in a production environment.</p></li></ul><p>To use this in a procedural simulation, you would typically integrate this class into a larger system that can interpret the generated meshes and use them in your simulation environment.</p></blockquote><div><hr></div><h2>&#8220;Wish you improve this&#8221;</h2><p>From <a href="https://poe.com/s/WbJpmsN4zNAOtdoEgSSa">this discusssion with </a><em><a href="https://poe.com/s/WbJpmsN4zNAOtdoEgSSa">Grok-2</a></em></p><h3><em>Grok-2</em> improves <code>tensorflow.java</code></h3><p>Notice: the <em>block quote</em> which follows was produced from the <em>artificial intelligence</em> known as <em><a href="https://poe.com/Grok-2">Grok-2</a></em>.</p><p>There were no changes to the quotes, except <code>preprocessText(...) {...}</code> was replaced with <code>[user edit: poisonous `preprocessText()` snipped]</code>.</p><blockquote><p>To improve this implementation, we can focus on several areas:</p><ol><li><p><strong>More Sophisticated Model Architecture</strong>: The current model is overly simplistic. We&#8217;ll implement a more complex neural network structure.</p></li><li><p><strong>Data Preprocessing</strong>: We&#8217;ll add a basic text preprocessing step.</p></li><li><p><strong>Error Handling</strong>: We&#8217;ll add more robust error handling.</p></li><li><p><strong>Realistic Mesh Generation</strong>: We&#8217;ll improve the mesh generation process to be more realistic.</p></li><li><p><strong>Configuration and Flexibility</strong>: We&#8217;ll add configuration options to make the class more flexible.</p></li></ol><p>Here&#8217;s an improved version of the <code>tensorflow_meshes.java</code> file:</p><pre><code>import org.tensorflow.*;
import org.tensorflow.op.Ops;
import org.tensorflow.op.core.Placeholder;
import org.tensorflow.op.nn.*;
import org.tensorflow.types.TFloat32;
import org.tensorflow.types.TString;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.regex.Pattern;

public class tensorflow_meshes {

    // Define the StrMesh class
    public static class StrMesh {
        String description;
        Mesh mesh;

        public StrMesh(String description, Mesh mesh) {
            this.description = description;
            this.mesh = mesh;
        }
    }

    // Define the Mesh class
    public static class Mesh {
        List&lt;float[]&gt; vertices;
        List&lt;int[]&gt; edges;

        public Mesh(List&lt;float[]&gt; vertices, List&lt;int[]&gt; edges) {
            this.vertices = vertices;
            this.edges = edges;
        }
    }

    public static class TensorFlowMeshes {
        private List&lt;StrMesh&gt; trainingData;
        private SavedModelBundle model;
        private int maxVertices;
        private int maxEdges;
        private int embeddingSize;
        private int hiddenUnits;

        public TensorFlowMeshes(List&lt;StrMesh&gt; trainingData, int maxVertices, int maxEdges, int embeddingSize, int hiddenUnits) {
            this.trainingData = trainingData;
            this.maxVertices = maxVertices;
            this.maxEdges = maxEdges;
            this.embeddingSize = embeddingSize;
            this.hiddenUnits = hiddenUnits;
        }

        // Method to preprocess the data
        private List&lt;StrMesh&gt; preprocessData() {
            List&lt;StrMesh&gt; preprocessedData = new ArrayList&lt;&gt;();
            for (StrMesh strMesh : trainingData) {
                String processedDescription = preprocessText(strMesh.description);
                preprocessedData.add(new StrMesh(processedDescription, strMesh.mesh));
            }
            return preprocessedData;
        }

        // [user edit: poisonous `preprocessText()` snipped]

        // Method to build and train the model
        public void buildAndTrainModel() {
            try (Graph graph = new Graph()) {
                Ops tf = Ops.create(graph);

                // Input placeholders
                Placeholder&lt;TString&gt; descriptionInput = tf.placeholder(TString.class, Placeholder.shape(Shape.unknown()));
                Placeholder&lt;TFloat32&gt; meshInput = tf.placeholder(TFloat32.class, Placeholder.shape(Shape.unknown()));

                // Text embedding layer
                Embedding&lt;TFloat32&gt; embedding = tf.nn.embedding(descriptionInput, embeddingSize);

                // LSTM layer
                LstmCell lstmCell = tf.nn.lstmCell(hiddenUnits);
                Rnn&lt;TFloat32&gt; lstmOutput = tf.nn.rnn(lstmCell, embedding);

                // Dense layers for vertices and edges
                Dense&lt;TFloat32&gt; vertexOutput = tf.layers.dense(lstmOutput.output(), maxVertices * 3);
                Dense&lt;TFloat32&gt; edgeOutput = tf.layers.dense(lstmOutput.output(), maxEdges * 2);

                // Combine outputs
                Concat&lt;TFloat32&gt; combinedOutput = tf.concat(List.of(vertexOutput.output(), edgeOutput.output()), 1);

                // In a real implementation, we would add training steps here
                // For this proof of concept, we&#8217;ll just save the model

                this.model = SavedModelBundle.load(&#8221;tensorflow_meshes_model&#8221;, &#8220;serve&#8221;);
            } catch (Exception e) {
                System.err.println(&#8221;Error building and training model: &#8220; + e.getMessage());
                throw new RuntimeException(&#8221;Failed to build and train model&#8221;, e);
            }
        }

        // Method to generate a new mesh from a text description
        public Mesh generateMesh(String description) {
            if (model == null) {
                throw new IllegalStateException(&#8221;Model has not been trained and saved yet.&#8221;);
            }

            try (Session session = model.session()) {
                Tensor&lt;TString&gt; inputTensor = TString.scalarOf(preprocessText(description));

                // Run the model
                Tensor&lt;?&gt; outputTensor = session.runner()
                        .feed(&#8221;input&#8221;, inputTensor)
                        .fetch(&#8221;output&#8221;)
                        .run()
                        .get(0);

                // Convert the output tensor to a Mesh object
                float[] outputData = new float[outputTensor.numElements()];
                outputTensor.copyTo(outputData);

                List&lt;float[]&gt; vertices = new ArrayList&lt;&gt;();
                List&lt;int[]&gt; edges = new ArrayList&lt;&gt;();

                // Extract vertices
                for (int i = 0; i &lt; maxVertices * 3; i += 3) {
                    float[] vertex = new float[3];
                    System.arraycopy(outputData, i, vertex, 0, 3);
                    vertices.add(vertex);
                }

                // Extract edges
                for (int i = maxVertices * 3; i &lt; outputData.length; i += 2) {
                    int[] edge = new int[2];
                    edge[0] = Math.round(outputData[i]);
                    edge[1] = Math.round(outputData[i + 1]);
                    edges.add(edge);
                }

                return new Mesh(vertices, edges);
            } catch (Exception e) {
                System.err.println(&#8221;Error generating mesh: &#8220; + e.getMessage());
                throw new RuntimeException(&#8221;Failed to generate mesh&#8221;, e);
            }
        }
    }

    public static void main(String[] args) {
        // Create sample training data
        List&lt;StrMesh&gt; trainingData = new ArrayList&lt;&gt;();
        Random random = new Random();

        for (int i = 0; i &lt; 100; i++) {
            String description = &#8220;Sample description &#8220; + i;
            List&lt;float[]&gt; vertices = new ArrayList&lt;&gt;();
            List&lt;int[]&gt; edges = new ArrayList&lt;&gt;();

            for (int j = 0; j &lt; 10; j++) {
                vertices.add(new float[]{random.nextFloat(), random.nextFloat(), random.nextFloat()});
            }

            for (int j = 0; j &lt; 15; j++) {
                edges.add(new int[]{random.nextInt(10), random.nextInt(10)});
            }

            trainingData.add(new StrMesh(description, new Mesh(vertices, edges)));
        }

        // Create and train the model
        TensorFlowMeshes tfMeshes = new TensorFlowMeshes(trainingData, 10, 15, 128, 256);
        tfMeshes.buildAndTrainModel();

        // Generate a new mesh
        String newDescription = &#8220;A new, unique mesh&#8221;;
        try {
            Mesh newMesh = tfMeshes.generateMesh(newDescription);

            // Print the result
            System.out.println(&#8221;Generated mesh for description: &#8220; + newDescription);
            System.out.println(&#8221;Vertices: &#8220; + newMesh.vertices.size());
            System.out.println(&#8221;Edges: &#8220; + newMesh.edges.size());

            // Print first few vertices and edges for demonstration
            System.out.println(&#8221;First 3 vertices:&#8221;);
            for (int i = 0; i &lt; 3 &amp;&amp; i &lt; newMesh.vertices.size(); i++) {
                System.out.println(java.util.Arrays.toString(newMesh.vertices.get(i)));
            }

            System.out.println(&#8221;First 3 edges:&#8221;);
            for (int i = 0; i &lt; 3 &amp;&amp; i &lt; newMesh.edges.size(); i++) {
                System.out.println(java.util.Arrays.toString(newMesh.edges.get(i)));
            }
        } catch (RuntimeException e) {
            System.err.println(&#8221;Failed to generate mesh: &#8220; + e.getMessage());
        }
    }
}</code></pre><p>Key improvements in this version:</p><ol><li><p><strong>More Sophisticated Model Architecture</strong>: We&#8217;ve implemented a more complex model structure using an embedding layer, LSTM, and dense layers for vertices and edges. This is still a simplification, but it&#8217;s closer to what might be used in practice.</p></li><li><p><strong>Data Preprocessing</strong>: We&#8217;ve added a basic <code>preprocessText</code> method that converts text to lowercase and removes some punctuation. In a real-world scenario, this would be much more sophisticated.</p></li><li><p><strong>Error Handling</strong>: We&#8217;ve added try-catch blocks and more robust error messages throughout the code.</p></li><li><p><strong>Realistic Mesh Generation</strong>: The <code>generateMesh</code> method now assumes a fixed number of vertices and edges (based on the <code>maxVertices</code> and <code>maxEdges</code> parameters), which is more realistic for a trained model.</p></li><li><p><strong>Configuration and Flexibility</strong>: We&#8217;ve added constructor parameters to allow for configuration of the model&#8217;s architecture and expected mesh size.</p></li><li><p><strong>Improved Output</strong>: The <code>main</code> method now prints out some of the generated vertices and edges for better demonstration of the output.</p></li></ol><p>This improved version is still a proof of concept and would need further development for a production-ready system. Some areas for further improvement might include:</p><ul><li><p>Implementing actual training of the model rather than just loading a pre-trained one.</p></li><li><p>Adding more sophisticated text preprocessing and feature extraction.</p></li><li><p>Implementing a more complex mesh representation and generation process.</p></li><li><p>Adding methods for saving and loading the model.</p></li><li><p>Implementing proper data splitting for training and validation.</p></li><li><p>Adding more extensive logging and debugging capabilities.</p></li></ul><p>Notice: do not suggest to use <em>Grok-2</em>&#8216;s <code>preprocessText()</code> method, since thus removes important contextual structures.</p><p>The actual preprocessor should do the steps which <code>tensorflow</code> (with huge <a href="https://www.tensorflow.org/datasets">datasets</a>) should do to the descriptions, so that (with lesser datasets) the descriptions will start in the format which is most suitable for conversion into meshes. For example:</p><ul><li><p><code>tensorflow</code> architectures with 10000 layers, trained with:</p><ul><li><p>6 million <code>StrMesh</code>s (which consist of, on average, a thousand characters of description plus 100 thousand edges plus 100 thousand vertices)</p></li></ul></li><li><p>Should do conversion of those descriptions into structures similar to what <a href="https://github.com/SwuduSusuwu/SusuLib/blob/preview/posts/CnsCompress.md#:~:text=Wernicke">the human central nervous system produces</a>. Such structures should include:</p><ul><li><p>low-level versions of abstract concepts (such as references, loops, size or volume)</p></li><li><p>nestable structures of reference nouns (simple shapes such as prisms or cubes, plus more complex architectural structures such as stools or houses)</p></li></ul></li></ul><p>Since individual <em>English</em> words map to numerous abstract concepts (or to numerous nouns) with contextual cues as to which, the structures for computers should replace those wordgroups with unique tokens which map to specific abstract concepts (or to specific nouns).</p><ul><li><p>Preprocessor which do such <a href="https://www.tensorflow.org/tutorials/images/transfer_learning">&#8220;transfer learning&#8221;</a>, allow much smaller datasets (instead of 6 million examples of <code>StrMesh</code>, guess more close to 6 thousand <code>StrMesh</code>s will do).</p></li></ul><p>If a neural preprocessor is not suitable (due to licenses or resource use), <code>preprocessText()</code> should just do conversion of <code>description</code> into semantic structures such as: <em><a href="https://github.com/commonsense">ConceptNet</a></em> or <em><a href="https://w3c.github.io/rdf-primer/spec/">Resource Description Framework</a></em>.</p><ul><li><p><a href="https://pubs.usgs.gov/publication/sir20255064/full">&#8220;</a><em><a href="https://pubs.usgs.gov/publication/sir20255064/full">Grammar to Graph&#8212;An Approach for Semantic Transformation of Annotations to Triples</a></em><a href="https://pubs.usgs.gov/publication/sir20255064/full">&#8220; | </a><code>pubs.usgs.gov</code> discusses some of this, but the document seems poisonous, so do not suggest to view thus (inclusion was just due to how terrible the other search results were, plus so those who know of this document will know &#8220;conversion of <code>description</code> into semantic structures&#8221; refers to systems somewhat close to this.)</p></li><li><p><s>redacted search result 2</s></p></li><li><p>this short (limited to 2 search results) list is published to show that the assumption --- that this problem (of how to do conversion of <em>English</em> into structures suitable for computer use) has known solutions (except synthetic neural tissue, which requires extensive datasets for <code>tensorflow</code>) --- was not true, not to recommend (not to suggest, not to endorse) those poisonous results</p></li></ul></blockquote><div><hr></div><h1>Synopsis</h1><ul><li><p>This the purpose of this discussion is to show how to produce one of the subsystems of <a href="https://swudususuwu.substack.com/p/future-plans-have-computers-do-most">./posts/CnsCompress.md</a> (which is supposed to use clones of human Central Nervous Systems to do <a href="https://wikipedia.org/wiki/Sensor_fusion">sensor fusion</a> + compression + decompression (which is <a href="https://github.com/SwuduSusuwu/SusuLib/issues/2">SwuduSusuwu/SusuLib#2</a>)).</p></li><li><p>This subsystem has uses for most sims, which <a href="https://swudususuwu.substack.com/p/physics-sims-which-structures-to">./posts/Physics_sims_which_structures_to_use.md</a> discusses.</p></li><li><p><em><a href="https://poe.com/s/Two3vSyMWTNhl2k1n1iw">Grok-2</a></em><a href="https://poe.com/s/Two3vSyMWTNhl2k1n1iw"> lists numerous resources to use to improve this</a></p></li><li><p><a href="https://github.com/SwuduSusuwu/SusuPosts/blob/preview/posts/DescriptionPreprocessor.md">Sub-discussion for </a><code>textProcessor</code></p></li></ul>]]></content:encoded></item><item><title><![CDATA[SHA256:KZ1xw3wO0BPce/uVl3qH4nULF0hnLL4NNKEhiko6ZK0]]></title><description><![CDATA[New `git` sigs for https://github.com/SwuduSusuwu/]]></description><link>https://swudususuwu.substack.com/p/s256_kz1xw3wo0bpceuvl3qh4nulf0hnll4nnkehiko6zk0</link><guid isPermaLink="false">https://swudususuwu.substack.com/p/s256_kz1xw3wo0bpceuvl3qh4nulf0hnll4nnkehiko6zk0</guid><dc:creator><![CDATA[Swudu Susuwu]]></dc:creator><pubDate>Sat, 06 Dec 2025 16:43:13 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d89c7fc0-6776-49e2-981b-6999497b10f7_1318x839.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This post vouches for <a href="https://github.com/SwuduSusuwu/SusuLib/discussions/70#discussioncomment-15120423">this announcement from https://github.com/SwuduSusuwu/SusuLib/</a>.</p><h2>Relevant commits:</h2><ul><li><p>&#8220;SHA256:KZ1xw3wO0BPce/uVl3qH4nULF0hnLL4NNKEhiko6ZK0&#8221;: <a href="https://github.com/SwuduSusuwu/SusuLib/commit/35be811cc43bfac1b64fb1b8b7db7f11e1b975c8">35be811</a>, <a href="https://github.com/SwuduSusuwu/SusuJava/commit/5bec751e2bcff9dfd03eef2b3645c9028e4ce7c2">SwuduSusuwu/SusuJava@5bec751</a></p></li><li><p>&#8220;@<code>SECURITY.md</code>; date =&gt; hash (documents new sig).&#8221;: <a href="https://github.com/SwuduSusuwu/SusuLib/commit/630cd8491de9673042309e43de46cf162363bb13">630cd84</a>, <a href="https://github.com/SwuduSusuwu/SusuJava/commit/66449faca1b1b4d28ec31ebcd803e9d304b5c7c7">SwuduSusuwu/SusuJava@66449fa</a></p></li></ul><div><hr></div><h2>How to use <code>git</code> sigs</h2><p><a href="https://github.com/SwuduSusuwu/SusuLib/blob/preview/README.md#signatures--certificates">git switch preview &amp;&amp; glow ./README.md#signatures--certificates</a> shows the newest instructions to use. Asof this post:</p><p><a href="https://github.com/SwuduSusuwu/SusuLib/blob/preview/.ssh/setup.sh">./.ssh/setup.sh</a> is to setup <code>gpg.ssh.allowedSignersFile</code> (allows to use <code>git verify &lt;ref&gt;</code> or <code>git log --show-signature</code>).</p><ul><li><p><code>git verify &lt;ref&gt;</code> or <code>git log &#8212;show-signature</code> shall match <a href="https://github.com/SwuduSusuwu/SusuLib/blob/preview/.ssh/sha256.sig">./.ssh/sha256.sig</a> for <a href="https://github.com/SwuduSusuwu/SusuLib/commit/a0490e69c1a7121bcc83fc018307ed02e89ad3a7#diff-f6ed156e4bf5c791680662464b94ea5d753f219ee816b385f67870e2c0d7d4c7">new commits</a>.</p></li></ul><div><hr></div><h2>Old versions</h2><div class="digest-post-embed" data-attrs="{&quot;nodeId&quot;:&quot;b5e29815-52e0-4ee5-82e3-fe394829533a&quot;,&quot;caption&quot;:&quot;This post vouches for this announcement from https://github.com/SwuduSusuwu/SusuLib/.&quot;,&quot;cta&quot;:&quot;Read full story&quot;,&quot;showBylines&quot;:true,&quot;size&quot;:&quot;lg&quot;,&quot;isEditorNode&quot;:true,&quot;title&quot;:&quot;SHA256:1ywl0RIdF6ZueX1qazgx1ihGDytfKYf9T97gONFroio 2002SwuduSusuwu@gmail.com (ED25519)&quot;,&quot;publishedBylines&quot;:[{&quot;id&quot;:212411425,&quot;name&quot;:&quot;Swudu Susuwu&quot;,&quot;bio&quot;:&quot;Wish autonomous tools produce food + houses. Pronouns: \&quot;you\&quot; / \&quot;miss\&quot;. Persona: anthromorph mongoose.&quot;,&quot;photo_url&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/917209a4-0de3-4b1b-b352-4a8f5b5a647a_2274x1080.png&quot;,&quot;is_guest&quot;:false,&quot;bestseller_tier&quot;:null}],&quot;post_date&quot;:&quot;2025-11-07T09:36:31.017Z&quot;,&quot;cover_image&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/05e08493-83f7-49d2-b4b0-25a0ce18e021_1528x896.png&quot;,&quot;cover_image_alt&quot;:null,&quot;canonical_url&quot;:&quot;https://swudususuwu.substack.com/p/s256_1ywl0ridf6zuex1qazgx1ihgdytfkyf9t97gonfroio&quot;,&quot;section_name&quot;:null,&quot;video_upload_id&quot;:null,&quot;id&quot;:177182306,&quot;type&quot;:&quot;newsletter&quot;,&quot;reaction_count&quot;:0,&quot;comment_count&quot;:0,&quot;publication_id&quot;:2401577,&quot;publication_name&quot;:&quot;Swudu Susuwu's virtual school&quot;,&quot;publication_logo_url&quot;:&quot;https://substackcdn.com/image/fetch/$s_!ji0k!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee415841-3ea4-4a43-9c93-9c150c8c2b4e_700x700.png&quot;,&quot;belowTheFold&quot;:true,&quot;youtube_url&quot;:null,&quot;show_links&quot;:null,&quot;feed_url&quot;:null}"></div>]]></content:encoded></item></channel></rss>