Skip to content

Commit 81135fc

Browse files
committed
Merge remote-tracking branch 'origin/dev' into branp/198/user-select-add-joints
2 parents 44e05e4 + 733c845 commit 81135fc

40 files changed

Lines changed: 827 additions & 292 deletions

exporter/SynthesisFusionAddin/src/Dependencies.py

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ def getInternalFusionPythonInstillationFolder() -> str | os.PathLike[str]:
4040
return folder
4141

4242

43-
def executeCommand(*args: str) -> subprocess.CompletedProcess[str]:
43+
def executeCommand(*args: str, check: bool = True) -> subprocess.CompletedProcess[str]:
4444
logger.debug(f"Running Command -> {' '.join(args)}")
4545
try:
4646
result: subprocess.CompletedProcess[str] = subprocess.run(
47-
args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=True
47+
args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=check
4848
)
4949
logger.debug(f"Command Output:\n{result.stdout}")
5050
return result
@@ -55,6 +55,40 @@ def executeCommand(*args: str) -> subprocess.CompletedProcess[str]:
5555
raise error
5656

5757

58+
def pipInstalled(pythonExecutablePath: str) -> bool:
59+
"""Check whether pip is importable by the given Fusion python interpreter."""
60+
# Use check=False so the expected "pip missing" case does not log an error.
61+
return executeCommand(pythonExecutablePath, "-m", "pip", "--version", check=False).returncode == 0
62+
63+
64+
def ensurePipInstalled(pythonFolder: str | os.PathLike[str], pythonExecutablePath: str) -> None:
65+
"""Bootstrap pip into Fusion's bundled python if it is missing.
66+
67+
Fusion's runtime does not reliably ship pip: it is absent by default on macOS, and every
68+
Fusion update lands in a fresh 'webdeploy' folder whose python may also lack it on Windows.
69+
Prefer the stdlib 'ensurepip' (no network required); fall back to downloading 'get-pip.py'.
70+
"""
71+
if pipInstalled(pythonExecutablePath):
72+
return
73+
74+
logger.info("pip not found in Fusion python, bootstrapping...")
75+
76+
# ensurepip ships with the CPython standard library and needs no internet access.
77+
try:
78+
executeCommand(pythonExecutablePath, "-m", "ensurepip", "--upgrade")
79+
if pipInstalled(pythonExecutablePath):
80+
return
81+
except subprocess.CalledProcessError:
82+
logger.warning("ensurepip unavailable, falling back to get-pip.py")
83+
84+
# Fallback: download and run get-pip.py (curl ships with both modern Windows and macOS).
85+
pipInstallScriptPath = os.path.join(pythonFolder, "get-pip.py")
86+
if not os.path.exists(pipInstallScriptPath):
87+
executeCommand("curl", "-fsSL", "https://bootstrap.pypa.io/get-pip.py", "-o", pipInstallScriptPath)
88+
89+
executeCommand(pythonExecutablePath, pipInstallScriptPath)
90+
91+
5892
def getInstalledPipPackages(pythonExecutablePath: str) -> dict[str, str]:
5993
result: str = executeCommand(pythonExecutablePath, "-m", "pip", "freeze").stdout
6094
# We don't need to check against packages with a specific hash as those are not required by Synthesis.
@@ -92,17 +126,11 @@ def resolveDependencies() -> bool | None:
92126
progressBar.reset()
93127
progressBar.show("Synthesis", f"Installing dependencies...", 0, len(PIP_DEPENDENCY_VERSION_MAP) * 2 + 2, 0)
94128

95-
# Install pip manually on macos as it is not included by default? Really?
96-
if SYSTEM == "Darwin" and not os.path.exists(os.path.join(pythonFolder, "pip")):
97-
pipInstallScriptPath = os.path.join(pythonFolder, "get-pip.py")
98-
if not os.path.exists(pipInstallScriptPath):
99-
executeCommand("curl", "https://bootstrap.pypa.io/get-pip.py", "-o", pipInstallScriptPath)
100-
progressBar.message = "Downloading PIP Installer..."
101-
102-
progressBar.progressValue += 1
103-
progressBar.message = "Installing PIP..."
104-
executeCommand(pythonExecutablePath, pipInstallScriptPath)
105-
progressBar.progressValue += 1
129+
# Fusion's bundled python does not reliably ship pip (missing by default on macOS, and on
130+
# fresh Windows webdeploy installs after a Fusion update), so bootstrap it if it is absent.
131+
progressBar.message = "Installing PIP..."
132+
ensurePipInstalled(pythonFolder, pythonExecutablePath)
133+
progressBar.progressValue += 2
106134

107135
installedPackages = getInstalledPipPackages(pythonExecutablePath)
108136
if packagesOutOfDate(installedPackages):

fission/public/assetpack.zip

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:346732c52aeb501ca834604abd1e709b37739e411167f65966948c397c42b9b3
3-
size 41776228
2+
oid sha256:929579b2dc87cffb6b5236f054a6b1ffcb17f0a384f4262f58fd2cb99fdc48ac
3+
size 41776301

fission/src/mirabuf/MirabufSceneObject.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier {
378378
? defaultFieldSpawnLocation()
379379
: (this.robotSpawnPosition(referencePos) ?? defaultFieldSpawnLocation())
380380

381-
this.setObjectPosition(pos)
381+
this.setObjectPosition(pos, referencePos)
382382
}
383383

384384
private robotSpawnPosition(referencePos: THREE.Vector3): SpawnLocation | undefined {
@@ -390,8 +390,8 @@ class MirabufSceneObject extends SceneObject implements ContextSupplier {
390390
? fieldLocations[this.alliance][this.station]
391391
: fieldLocations?.default
392392

393-
// TODO
394-
// Why are we calling this?
393+
// Mutates referencePos in place (Box3.getCenter side effect) so setObjectPosition can offset
394+
// this spawn location by the field's own transform instead of treating it as a world-space position.
395395
field?.getXZPositionTransform(referencePos)
396396

397397
return pos

fission/src/systems/physics/ConstraintSettingsUtilities.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,17 @@ import { convertMirabufVector3ToJoltRVec3, convertMirabufVector3ToJoltVec3 } fro
66

77
type LimitSpecs = Omit<DOFSpecs, "friction" | "axis">
88

9-
// Returns a STATIC_ALIAS `RVec3.AddRVec3()`'s scratch buffer.
109
export function createAnchorPoint(jointInstance: mirabuf.joint.JointInstance, jointDefinition: mirabuf.joint.Joint) {
11-
const jointOrigin = jointDefinition.origin
10+
const anchorPoint = jointDefinition.origin
1211
? convertMirabufVector3ToJoltRVec3(jointDefinition.origin)
1312
: new JOLT.RVec3(0, 0, 0)
1413
// TODO: Offset transformation for robot builder.
1514
const jointOriginOffset = jointInstance.offset
16-
? convertMirabufVector3ToJoltRVec3(jointInstance.offset)
17-
: new JOLT.RVec3(0, 0, 0)
15+
? convertMirabufVector3ToJoltVec3(jointInstance.offset)
16+
: new JOLT.Vec3(0, 0, 0)
1817

19-
const anchorPoint = jointOrigin.AddRVec3(jointOriginOffset)
18+
anchorPoint.Add(jointOriginOffset)
2019

21-
JOLT.destroy(jointOrigin)
2220
JOLT.destroy(jointOriginOffset)
2321

2422
return anchorPoint

fission/src/systems/physics/PhysicsSystem.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,7 @@ class PhysicsSystem extends WorldSystem {
617617

618618
const anchorPoint = createAnchorPoint(jointInstance, jointDefinition)
619619
hingeConstraintSettings.mPoint1 = hingeConstraintSettings.mPoint2 = anchorPoint
620+
JOLT.destroy(anchorPoint)
620621

621622
const rotationalFreedom = jointDefinition.rotational!.rotationalFreedom!
622623

@@ -650,6 +651,7 @@ class PhysicsSystem extends WorldSystem {
650651

651652
const anchorPoint = createAnchorPoint(jointInstance, jointDefinition)
652653
constraintSettings.mPoint1 = constraintSettings.mPoint2 = anchorPoint
654+
JOLT.destroy(anchorPoint)
653655

654656
const freedom = jointDefinition.prismatic!.prismaticFreedom!
655657

@@ -858,6 +860,7 @@ class PhysicsSystem extends WorldSystem {
858860

859861
JOLT.destroy(axis)
860862
JOLT.destroy(unitAxis)
863+
JOLT.destroy(anchorPoint)
861864

862865
const vehicleConstraint = this.createVehicleConstraint(wheelSettings, bodyMain, maxAcc, urdfWheelBasis)
863866
const { listener, tester } = this.createVehicleListeners(vehicleConstraint, bodyWheel)
@@ -888,6 +891,7 @@ class PhysicsSystem extends WorldSystem {
888891
const dofs = jointDefinition.custom?.dofs
889892
if (!dofs || dofs.length < 3) {
890893
console.warn("Empty degrees-of-freedom in joint definition for ball constraint")
894+
JOLT.destroy(anchorPoint)
891895

892896
return
893897
}
@@ -941,6 +945,8 @@ class PhysicsSystem extends WorldSystem {
941945

942946
JOLT.destroy(constraintSpecifications.axis)
943947
})
948+
949+
JOLT.destroy(anchorPoint)
944950
}
945951

946952
/**
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
export type JoltBodyIndexAndSequence = number
22

33
export const PAUSE_REF_ASSEMBLY_SPAWNING = "assembly-spawning"
4-
export const PAUSE_REF_ASSEMBLY_CONFIG = "assembly-config"
54
export const PAUSE_REF_ASSEMBLY_MOVE = "assembly-move"

fission/src/systems/preferences/PreferenceTypes.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,10 @@ import type { SimConfigData } from "../simulation/SimConfigShared"
77
/** Names of all global preferences. */
88

99
export type UserPreferences = {
10-
ZoomSensitivity: number
11-
PitchSensitivity: number
12-
YawSensitivity: number
1310
SceneRotationSensitivity: number
1411
ScenePanSensitivity: number
1512
ViewCubeRotationSensitivity: number
1613
ReportAnalytics: boolean
17-
UseMetric: boolean
1814
RenderScoringZones: boolean
1915
RenderProtectedZones: boolean
2016
InputSchemes: InputScheme[]
@@ -51,14 +47,10 @@ export type Preferences = {
5147
*/
5248
export function defaultUserPreferences(): UserPreferences {
5349
return {
54-
ZoomSensitivity: 15,
55-
PitchSensitivity: 10,
56-
YawSensitivity: 3,
5750
SceneRotationSensitivity: 0.5,
5851
ScenePanSensitivity: 1.0,
5952
ViewCubeRotationSensitivity: 0.025,
6053
ReportAnalytics: false,
61-
UseMetric: false,
6254
RenderScoringZones: true,
6355
RenderProtectedZones: true,
6456
InputSchemes: [],
@@ -199,7 +191,7 @@ export type RobotPreferences = {
199191
cameras: CameraPreferences[]
200192
driveVelocity: number
201193
driveAcceleration: number
202-
unstickForce: number
194+
unstickStrength: number
203195
sequentialConfig?: SequentialBehaviorPreferences[]
204196
simConfig?: SimConfigData
205197
}
@@ -210,9 +202,11 @@ export type MotorPreferences = {
210202
maxAcceleration: number
211203
}
212204

213-
export type Alliance = "red" | "blue"
205+
export const ALLIANCES = ["red", "blue"] as const
206+
export type Alliance = (typeof ALLIANCES)[number]
214207

215-
export type Station = 1 | 2 | 3
208+
export const STATIONS = [1, 2, 3] as const
209+
export type Station = (typeof STATIONS)[number]
216210

217211
export type ZonePreferencesShared = {
218212
name: string
@@ -267,6 +261,9 @@ export type FieldPreferences = {
267261
cameraPoints: CameraPoint[]
268262
}
269263

264+
export const MIN_UNSTICK_STRENGTH = 0
265+
export const MAX_UNSTICK_STRENGTH = 5
266+
270267
export function defaultRobotPreferences(): RobotPreferences {
271268
return {
272269
inputsSchemes: [],
@@ -288,7 +285,7 @@ export function defaultRobotPreferences(): RobotPreferences {
288285
cameras: [],
289286
driveVelocity: 0,
290287
driveAcceleration: 0,
291-
unstickForce: 8000,
288+
unstickStrength: 1,
292289
}
293290
}
294291

fission/src/systems/simulation/synthesis_brain/SynthesisBrain.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,16 @@ class SynthesisBrain extends Brain {
197197
// Handle unstick
198198
const unstickPressed = InputSystem.getInput("unstick", this._brainIndex) === 1
199199
if (unstickPressed && !this._prevUnstickPressed) {
200-
this.applyUnstickForce()
200+
this.applyUnstickStrength()
201201
}
202202

203203
this._prevUnstickPressed = unstickPressed
204204
}
205205

206206
/**
207-
* Applies a small upward force to the robot's main body to help unstick it
207+
* Applies a small upward impulse to the robot's main body to help unstick it
208208
*/
209-
private applyUnstickForce(): void {
209+
private applyUnstickStrength(): void {
210210
const rootBodyId = this._mechanism.getBodyByNodeId(this._mechanism.rootBody)
211211
if (!rootBodyId) {
212212
console.warn("Could not find root body for unstick")
@@ -219,9 +219,16 @@ class SynthesisBrain extends Brain {
219219
return
220220
}
221221

222-
const unstickForce = new JOLT.Vec3(0, this._assembly.robotPreferences.unstickForce, 0)
223-
body.AddForce(unstickForce) // CLONE
224-
JOLT.destroy(unstickForce)
222+
const inverseMass = body.GetMotionProperties().GetInverseMass()
223+
if (inverseMass <= 0) {
224+
console.warn("Root body has no mass, skipping unstick")
225+
return
226+
}
227+
228+
const mass = 1.0 / inverseMass
229+
const unstickImpulse = new JOLT.Vec3(0, this._assembly.robotPreferences.unstickStrength * mass, 0)
230+
body.AddImpulse(unstickImpulse) // CLONE
231+
JOLT.destroy(unstickImpulse)
225232
}
226233

227234
public disable(): void {

fission/src/systems/sound/SoundPlayer.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import PreferencesSystem from "../preferences/PreferencesSystem"
88

99
const preloadSounds = [dropdownMenuSound, clickdownSound, clickupSound, checkdownSound, checkupSound]
1010
type SoundEffect = {
11-
onMouseDown?: () => void
12-
onMouseUp?: () => void
11+
onMouseDown?: (e?: MouseEvent | unknown) => void
12+
onMouseUp?: (e?: MouseEvent | unknown) => void
1313
}
1414
export class SoundPlayer {
1515
private readonly _audioContext = new AudioContext()
@@ -107,7 +107,18 @@ export class SoundPlayer {
107107
}
108108
public dropdownSoundEffects(): SoundEffect {
109109
return {
110-
onMouseDown: () => this.playDropdownSound(),
110+
onMouseDown: e => {
111+
if (
112+
typeof e == "object" &&
113+
e != null &&
114+
"target" in e &&
115+
e.target instanceof HTMLElement &&
116+
e.target.getAttribute("aria-disabled") === "true"
117+
) {
118+
return
119+
}
120+
void this.playDropdownSound()
121+
},
111122
}
112123
}
113124

fission/src/test/GetAssets.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import MirabufInstance from "@/mirabuf/MirabufInstance.ts"
33
import MirabufParser from "@/mirabuf/MirabufParser.ts"
44

55
export const ROBOT_MODELS = {
6-
DOZER: "/api/mira/robots/Dozer v11.mira",
6+
DOZER: "/api/mira/robots/Dozer v12.mira",
77
MULTI_JOINT: "/api/mira/private/Multi-Joint Wheels v0.mira",
88
} satisfies Record<string, string>
99

0 commit comments

Comments
 (0)