- Learn
- Tutorials
- Physics Labs
- Push Lab
- 4 Load and Measure
4 Load and Measure
Move weights between the carts, compare their response, and keep readings after the motion has changed.
Drag a weight onto a cart. N selects weights; 1 and 2 load the selected weight, and Delete returns it. Space pushes; T inspects tickets; H closes the sheet.
Open the downloaded file with File › Open Project... in Myth Engine.
4.1 The Idea
A weight leaves the rack and settles above Blue Cart. Give both carts the same push again. Blue answers differently now.
Last chapter, the carts had different masses that you could not rearrange. This chapter puts that difference in your hands. A cart’s mass, measured in kilograms, includes its body and the weights it carries.
The same impulse produces a smaller change in velocity when that mass is larger. You have already seen Copper lag behind Blue. Now you can change which cart carries the load and test your own prediction.
The gauges watch the carts as they move. A paper keeps what happened earlier. Think of a speedometer beside a photograph of its reading: one changes with the car, while the other keeps its moment.
Those two jobs must stay separate. A weight transfer stops the affected cart and ends its current comparison. An old paper still describes the push that happened before you moved the weight.
Each cart has named places for its load, called seats. A table remembers which weight occupies each seat. The pictures follow that arrangement; the native cart mass comes from the same record.
4.2 The Plan
Weight Pattern describes the rack. Prepare Workshop Weights creates its copies, and Select Workshop Weight gives each one an identity in Workshop Weights.
Transfer Workshop Weight changes that arrangement. It tells each affected cart that its load changed, and Change Native Cart Load updates the native mass.
Push Workshop Pair still measures the impulse itself. Record Pair Reading stores the result. The gauges follow current velocity; the printer and tickets read saved rows.
4.3 Set Up
Continue with your shared push controls and working Return button. Joining here? Download the Chapter 3 starting workshop, then open it through File > Open Project…. The finished Chapter 4 download above contains this chapter’s completed work. Keep the existing Game Settings. Add the new Commands and Project Globals from this setup card.
Leave the new runtime tables empty where their cards have no rows. The workshop will fill them when it runs. Enter the tuning rows now.
Make the new Object resources before placing the copies listed below. Weights and pinned tickets are created by their pattern Logic, so leave those out of the Scene. Their repeated positions belong to the tables.
N12DeleteTThis chapter has a longer quiet stretch. The records, transfer rules and weight Objects need one another before the rack can fill. The first running checkpoint comes after Game Rules is connected.
Keep Making what a card shows nearby for For Each Row, Declare Variable and component bindings. Make the Functions before choosing them as Actions in another resource.
4.4 Give the Bench Its Records
4.4.1 Push Readings Entry
A paper needs more than a final speed. It needs the mass and impulse that produced the change, along with both velocity readings. Push Readings Entry gives that saved observation its fields.
Keep the two cart names distinct. Each row will hold their answers to the same accepted push.
One row of the "Push Readings" Data Table.
4.4.2 Push Readings
The printer starts without a measurement to print. Create Push Readings with the structure above and leave its rows empty. Accepted pushes will add the observations.
4.4.3 Workshop Weights Entry
A weight can look the same as its neighbor while belonging to a different cart. Workshop Weights Entry records its identity, mass and occupied place. The picture cannot tell you which matching weight occupies a particular seat on a particular cart.
One row of the "Workshop Weights" Data Table.
4.4.4 Workshop Weights
Workshop Weights is the live arrangement of the load. Leave this table empty too. Each generated weight will register itself when it appears.
4.4.5 Weight Pattern Entry
The rack has repeated stacks, with room between them for your pointer. Weight Pattern Entry describes one stack. Its fields say which picture to create and how to space the copies.
One row of the "Weight Pattern" Data Table.
4.4.6 Weight Pattern
The same weight Object can appear in separate stacks. Each stack gets a row. Match the row names in the Entry cells, then fill their columns.
The object column holds the exact resource name as text. Before running the pattern, make the weight resources that its object column names exactly. The initial load belongs to these rows too. A long decimal tail on rackY comes from the stored coordinate calculation. Enter the card’s value. Those extra digits do not describe a more precise physical apparatus.
One Kilogram Weight121.14.10000000000000050.100.8One Kilogram Weight122.1254.10000000000000050.100.8Two Kilogram Weight243.154.1750.120.95Five Kilogram Weight524.1254.250.111.1Five Kilogram Weight525.154.250.101.14.4.7 Cart Load Settings Entry
A dropped weight needs a place above the cart rather than inside its wheels. Cart Load Settings Entry describes the seat spacing and the area where a drop counts. Those are different jobs.
One row of the "Cart Load Settings" Data Table.
4.4.8 Cart Load Settings
Create the named cart row in Cart Load Settings. Its capacity limits the occupied seats. The drop area gives your hand some room while the seat positions keep the finished load arranged.
4-1.1250.750.352.11.30.74.4.9 Reading Ticket Settings Entry
The pinned papers form another pattern. Reading Ticket Settings Entry gives their Object name and the spacing along the board. A saved slot will distinguish each copy.
One row of the "Reading Ticket Settings" Data Table.
4.4.10 Reading Ticket Settings
The papers row sets that pattern’s starting place. Enter it as the named row in Reading Ticket Settings, using the structure above. The ticket Object will come later, after its reading behavior is ready.
Reading Ticket18.951.77514.2The records are ready for their first weights, although the bench has no new movement yet.
4.5 Let the Load Change the Cart
Now the weights will count as mass.
4.5.1 Calculate Cart Load
Copper’s load should come from the weights actually attached to Copper. Calculate Cart Load walks the live arrangement and adds the matching weights to the body’s base mass. It returns a mass, without moving the cart.
Look for this cart’s weights. The caller decides what a change in that answer should do.
Returns the base cart mass plus registered weights attached to cartNumber. A nonpositive base uses 1 kg. Callers decide whether to retain motion or start a new comparison.
- Name
loadedMass- Value
baseMass
loadedMass <= 0- Variable
loadedMass- Value
1
- Table
Workshop Weights- Variable Name
"load"
load.cart == cartNumber- Variable
loadedMass- Operator
+=- Value
load.mass
- Value
loadedMass
4.5.2 Set Starting Cart Load
Opening the workshop and restoring a saved experiment both need the mass to match the load. Set Starting Cart Load asks for that total and applies it. Restored velocity stays alone.
Prepare Workshop Weights will send weightsPrepared after the rack is ready. The Load Event is for Myth’s saved runtime state, rather than the later portable setup files.
Derives native mass from baseMass and the saved live load arrangement at opening and after Load; keeps restored velocity.
Any Object- Action Name
startingMass- cartNumber
@self.cartNumber- baseMass
@self.baseMass
- Component Value
self.transform.mass- Value
startingMass.value
- Action Name
restoredMass- cartNumber
@self.cartNumber- baseMass
@self.baseMass
- Component Value
self.transform.mass- Value
restoredMass.value
4.5.3 Change Native Cart Load
Taking a weight off a moving cart ends that cart’s comparison. Change Native Cart Load receives cartLoadChanged, applies the new total and stops the affected body. Its latest reading becomes invalid.
The other cart keeps rolling. Earlier rows in Push Readings keep their original measurements too. This Component supplies the cartNumber and baseMass values that the starting-load Component also reads.
Changes this exact native cart mass and stops its affected comparison; the other cart and historical records stay unchanged.
Any Object- Action Name
changedMass- cartNumber
@self.cartNumber- baseMass
@self.baseMass
- Component Value
self.transform.mass- Value
changedMass.value
- X Velocity
0- Y Velocity
0
@self.cartNumber == 1- Variable
$blueReadingValid- Value
false
- Variable
$copperReadingValid- Value
false
4.5.4 Blue Cart
Blue gains both load Components, in the shown attachment order. They share this cart’s existing identity. Keep its lane and drag behavior as they were.
An attachment that uses an existing Variable reads the same Object value. There is no second cartNumber to add.
X 3.6, Y 0.93Blue Cart ArtX 3.6, Y 0.9X 3.6, Y 0.9ColliderColliderNumberConstant6.75NumberConstant10.975NumberConstant5.975NumberConstant20.875NumberConstant14.5.5 Copper Cart
Copper’s painted load is about to become movable weights. Remove the marked Rear Load, Middle Load and Front Load Sprites. The generated weights will take their places.
Keep Copper’s lower lane and identity while adding the two load Components. Until generation is connected, that bare cart is the expected picture.
X 3.6, Y 0.9103Copper Cart ArtX 3.6, Y 0.9X 3.6, Y 0.9ColliderColliderRear LoadWorkshop weight-26.04X -1.125, Y 0.825X 0.625, Y 0.95Middle LoadWorkshop weight-26.05X -0.375, Y 0.825X 0.625, Y 0.95Front LoadWorkshop weight-56.08X 0.375, Y 0.9X 0.75, Y 1.1NumberConstant6.75NumberConstant8.2NumberConstant5.975NumberConstant20.875NumberConstant2Both carts can answer a load change while keeping the rail behavior you already built.
4.6 Carry One Weight
The next pieces agree on where a weight belongs.
4.6.1 Transfer Workshop Weight
A weight leaves its old cart only when the requested destination can receive it. Transfer Workshop Weight checks the destination and finds a free seat before committing the move. A refused request leaves its place alone.
This is the chapter’s longest transfer rule. The search for an available seat occupies the opening stretch, ending at Action 15. The move occupies Actions 16 through 33; the affected-cart messages follow at Actions 34 through 41. Its Return reports success.
Destination zero means the rack; one means Blue, and two means Copper. freeSeat zero means no room; minus one marks the rack route. Later, a held weight uses cart minus one until release. Those values describe different jobs.
The rack is a destination too. Returning there detaches the picture from its cart and restores its own rack coordinates.
Moves the exact weight to its requested cart or original rack place. A free preferred seat wins; otherwise the first free seat is used. Full or missing destinations leave its current place unchanged. Returns whether the requested placement was accepted; callers choose the feedback.
- Name
accepted- Type
Boolean- Value
false
- Name
oldCart- Value
data("Workshop Weights","Weight " + number,"cart")
- Name
freeSeat- Value
0
- Name
seatCount- Value
round(data("Cart Load Settings","cart","capacity"))
destination == 0- Variable
freeSeat- Value
-1
seatCounttimes- Name
candidateSeat- Value
local.index + 1
- Name
occupied- Value
0
- Table
Workshop Weights- Variable Name
"occupant"
occupant.cart == destination && occupant.seat == candidateSeat- Variable
occupied- Value
1
occupied == 0 && (freeSeat == 0 || candidateSeat == preferredSeat)- Variable
freeSeat- Value
candidateSeat
oldCart == destination- Variable
$pushStatus- Value
'That weight is already there.'
- Variable
accepted- Value
true
freeSeat != 0 && (destination == 0 || (destination == 1 && $blueCart.count == 1) || (destination == 2 && $copperCart.count == 1))- Applies to
weight
destination == 0- Applies to
weight- X
rackX- Y
rackY
destination == 1- Applies to
weight- Parent
$blueCart- Keep World Position
false- Local X
data("Cart Load Settings","cart","firstSeatX") + (freeSeat - 1) * data("Cart Load Settings","cart","spacing")- Local Y
data("Cart Load Settings","cart","cartTop") + height / 2- Destroy with Parent
false
- Applies to
weight- Parent
$copperCart- Keep World Position
false- Local X
data("Cart Load Settings","cart","firstSeatX") + (freeSeat - 1) * data("Cart Load Settings","cart","spacing")- Local Y
data("Cart Load Settings","cart","cartTop") + height / 2- Destroy with Parent
false
- Table
Workshop Weights- Row
"Weight " + number- Column
"cart"- Value
destination
destination == 0- Table
Workshop Weights- Row
"Weight " + number- Column
"seat"- Value
0
- Table
Workshop Weights- Row
"Weight " + number- Column
"seat"- Value
freeSeat
oldCart == 1- Applies to
$blueCart- Custom Event
cartLoadChanged
oldCart == 2- Applies to
$copperCart- Custom Event
cartLoadChanged
destination == 1- Applies to
$blueCart- Custom Event
cartLoadChanged
destination == 2- Applies to
$copperCart- Custom Event
cartLoadChanged
- Variable
$pushStatus- Value
'Load changed. Push for a fresh comparison.'
- Variable
accepted- Value
true
- Variable
$pushStatus- Value
'That cart is full or unavailable.'
- Value
accepted
4.6.2 Place Weight From Hand
Your hand can let go above a full cart. Place Weight From Hand asks the transfer rule for that drop, then restores the previous place if the destination refuses it. The weight has somewhere to return.
A deliberate accepted drop can click. Interruption recovery stays silent, so returning to the workshop does not sound like another experiment.
Places a released weight at its requested seat or restores its previous place. Only a deliberate drop opts into the quiet click; interruption recovery is silent.
data("Workshop Weights","Weight " + number,"cart") == -1- Action Name
placedWeightRequest1- weight
weight- number
number- destination
destination- rackX
rackX- rackY
rackY- height
height- preferredSeat
preferredSeat
- accepted
placedWeightRequest1.value && playClick
data("Workshop Weights","Weight " + number,"cart") == -1- Action Name
placedWeightRequest2- weight
weight- number
number- destination
previousCart- rackX
rackX- rackY
rackY- height
height- preferredSeat
previousSeat
- accepted
placedWeightRequest2.value && playClick
data("Workshop Weights","Weight " + number,"cart") == -1- Action Name
rackWeightRequest3- weight
weight- number
number- destination
0- rackX
rackX- rackY
rackY- height
height- preferredSeat
0
- accepted
rackWeightRequest3.value && playClick
4.6.3 Show Weight Selection
The selected weight gets a tint so you can find it again among its neighbors. Show Weight Selection compares the saved selection with this weight’s number. Looking at a weight changes no load.
The status also names that selected mass on the optional readings page, beside the other experiment feedback.
Shows one saved weight selection with a tunable Sprite tint and native status caption.
StructuraltransformTransformspriteSprite$selectedWeightNumber == number && number > 0- Applies to
weight- Component Value
weight.sprite.tint- Value
selectedTint
- Variable
$pushStatus- Value
"Selected " + mass + " kg weight " + number + "."
- Applies to
weight- Component Value
weight.sprite.tint- Value
"#ffffff"
4.6.4 Select Workshop Weight
A new weight needs an identity. Select Workshop Weight records that identity and its starting information in the live table. Its Mouse Press chooses this exact copy.
A changed selection refreshes the tint. Load refreshes it too, while Destroy clears a selection that has lost its weight. The shared pressSelected name still belongs to one Object value.
Declare the card’s Sprite reference through Components > Add Component > Sprite, with Name sprite. The self. prefix appears automatically. Move Selected Weight needs that reference too. Each weight Object below will bind both references to its attached picture component.
Selects the exact pressed generated weight and shows the same saved selection on the bench. Selection does not change cart mass, motion or readings.
Any ObjectSprite- Variable
$weightCount- Operator
+=- Value
1
- Variable
weightNumber- Value
$weightCount
- Action Name
addedRow- Table
Workshop Weights- Row
"Weight " + self.weightNumber- Values > number
self.weightNumber- Values > cart
0- Values > seat
0- Values > mass
$preparingWeightMass
- Variable
rackX- Value
$preparingRackX
- Variable
rackY- Value
$preparingRackY
- Variable
weightHeight- Value
$preparingHeight
self.pressSelected == true- Variable
$selectedWeightNumber- Value
self.weightNumber
- weight
self- number
self.weightNumber- mass
data("Workshop Weights","Weight " + self.weightNumber,"mass")
- weight
self- number
self.weightNumber- mass
data("Workshop Weights","Weight " + self.weightNumber,"mass")
$selectedWeightNumber == self.weightNumber- Variable
$selectedWeightNumber- Value
0
- Variable
$pushStatus- Value
'Selected weight removed. N selects another weight.'
4.6.5 Move Selected Weight
The keyboard can place the same weight your pointer selects. Move Selected Weight listens for the load and rack Commands, then checks this copy’s saved identity. Only the selected copy answers.
Each route calls the transfer Function. Removing a weight also removes its registry row, so later selection cannot find a vanished load.
Moves this weight only when its saved number is selected. Keys 1 and 2 choose a cart; Delete returns it to the rack.
Any ObjectSprite$selectedWeightNumber > 0 && $selectedWeightNumber == self.weightNumber && dataFind("Workshop Weights","cart",-1) < 0- Action Name
blueWeightRequest1- weight
self- number
self.weightNumber- destination
1- rackX
self.rackX- rackY
self.rackY- height
self.weightHeight
- accepted
blueWeightRequest1.value
$selectedWeightNumber > 0 && $selectedWeightNumber == self.weightNumber && dataFind("Workshop Weights","cart",-1) < 0- Action Name
copperWeightRequest2- weight
self- number
self.weightNumber- destination
2- rackX
self.rackX- rackY
self.rackY- height
self.weightHeight
- accepted
copperWeightRequest2.value
$selectedWeightNumber > 0 && $selectedWeightNumber == self.weightNumber && dataFind("Workshop Weights","cart",-1) < 0- Action Name
rackWeightRequest3- weight
self- number
self.weightNumber- destination
0- rackX
self.rackX- rackY
self.rackY- height
self.weightHeight
- accepted
rackWeightRequest3.value
- Name
oldCart- Value
data("Workshop Weights","Weight " + self.weightNumber,"cart")
- Action Name
addedRow- Operation
Remove- Table
Workshop Weights- Row
"Weight " + self.weightNumber
oldCart == 1- Applies to
$blueCart- Custom Event
cartLoadChanged
oldCart == 2- Applies to
$copperCart- Custom Event
cartLoadChanged
4.6.6 Drag Workshop Weight
Pick a weight near its edge. It keeps that point under your pointer as you lift it from the load. Drag Workshop Weight remembers its former place.
Cart Press Input supplies pressSelected, weightGrabOffsetX and weightGrabOffsetY at the exact press. Listen for Control Press connects each weight through Capture Control Press, using the existing supplied Script. Keep that listener on every weight Object, as its card shows. Replace the Library source in Fill the rack before testing pointer dragging; its updated helper recognizes weights.
Prepare While the Left Mouse Button Is Held with Released and Holding before filling any Switch Mode that targets it. Keep Released as the starting Mode. Its Actions can wait for the next panel.
Grabs the exact weight with its pointer offset. A held weight leaves its old load; release chooses the nearest available seat, and interruptions restore the previous place.
Any Objectself.pressSelected == true && dataFind("Workshop Weights","cart",-1) < 0 && ($blueCart.count == 0 || $blueCart.collider.fixed == false) && ($copperCart.count == 0 || $copperCart.collider.fixed == false)- Variable
previousCart- Value
data("Workshop Weights","Weight " + self.weightNumber,"cart")
- Variable
previousSeat- Value
data("Workshop Weights","Weight " + self.weightNumber,"seat")
- Name
startX- Value
self.transform.position.x
- Name
startY- Value
self.transform.position.y
- Action Name
transferWorkshopWeightResult- weight
self- number
self.weightNumber- rackX
self.rackX- rackY
self.rackY- height
self.weightHeight- destination
0
- Table
Workshop Weights- Row
"Weight " + self.weightNumber- Column
"cart"- Value
-1
- X
startX- Y
startY
- Event
While the Left Mouse Button Is Held- Mode
Holding
Released starts empty. Holding follows the pointer with both grab offsets, so the weight stays beneath the place you touched.
- X
clamp(valueMember(event.position,"x") - self.weightGrabOffsetX,.6,23.4)- Y
clamp(valueMember(event.position,"y") - self.weightGrabOffsetY,.6,15.4)
Release asks Place Weight From Hand for the nearest available seat inside a cart’s drop area. Outside those areas, it requests the rack. A weight being held prevents another pair push.
data("Workshop Weights","Weight " + self.weightNumber,"cart") == -1- Name
dropCart- Value
0
- Name
dropSeat- Value
0
$blueCart.count == 1 && abs(valueMember(event.position,"x") - $blueCart.transform.position.x) < data("Cart Load Settings","cart","dropHalfWidth") && abs(valueMember(event.position,"y") - $blueCart.transform.position.y - data("Cart Load Settings","cart","dropRise")) < data("Cart Load Settings","cart","dropHalfHeight")- Variable
dropCart- Value
1
- Variable
dropSeat- Value
clamp(round((valueMember(event.position,"x") - $blueCart.transform.position.x - data("Cart Load Settings","cart","firstSeatX")) / data("Cart Load Settings","cart","spacing")) + 1,1,data("Cart Load Settings","cart","capacity"))
$copperCart.count == 1 && abs(valueMember(event.position,"x") - $copperCart.transform.position.x) < data("Cart Load Settings","cart","dropHalfWidth") && abs(valueMember(event.position,"y") - $copperCart.transform.position.y - data("Cart Load Settings","cart","dropRise")) < data("Cart Load Settings","cart","dropHalfHeight")- Variable
dropCart- Value
2
- Variable
dropSeat- Value
clamp(round((valueMember(event.position,"x") - $copperCart.transform.position.x - data("Cart Load Settings","cart","firstSeatX")) / data("Cart Load Settings","cart","spacing")) + 1,1,data("Cart Load Settings","cart","capacity"))
- weight
self- number
self.weightNumber- rackX
self.rackX- rackY
self.rackY- height
self.weightHeight- destination
dropCart- preferredSeat
dropSeat- previousCart
self.previousCart- previousSeat
self.previousSeat- playClick
Yes
- Variable
$selectedWeightNumber- Value
self.weightNumber
- Event
While the Left Mouse Button Is Held- Mode
Released
Focus Lost, Touch Cancelled, Load, Return and opening Help all restore an interrupted hand. These are the same cleanup moments you met with the carts. Each uses the same recovery call and Released destination; compare the trigger names as you repeat them. Prepare each named Event first.
- weight
self- number
self.weightNumber- rackX
self.rackX- rackY
self.rackY- height
self.weightHeight- destination
self.previousCart- preferredSeat
self.previousSeat- previousCart
self.previousCart- previousSeat
self.previousSeat- playClick
No
- Event
While the Left Mouse Button Is Held- Mode
Released
- weight
self- number
self.weightNumber- rackX
self.rackX- rackY
self.rackY- height
self.weightHeight- destination
self.previousCart- preferredSeat
self.previousSeat- previousCart
self.previousCart- previousSeat
self.previousSeat- playClick
No
- Event
While the Left Mouse Button Is Held- Mode
Released
- weight
self- number
self.weightNumber- rackX
self.rackX- rackY
self.rackY- height
self.weightHeight- destination
self.previousCart- preferredSeat
self.previousSeat- previousCart
self.previousCart- previousSeat
self.previousSeat- playClick
No
- Event
While the Left Mouse Button Is Held- Mode
Released
- weight
self- number
self.weightNumber- rackX
self.rackX- rackY
self.rackY- height
self.weightHeight- destination
self.previousCart- preferredSeat
self.previousSeat- previousCart
self.previousCart- previousSeat
self.previousSeat- playClick
No
- Event
While the Left Mouse Button Is Held- Mode
Released
- weight
self- number
self.weightNumber- rackX
self.rackX- rackY
self.rackY- height
self.weightHeight- destination
self.previousCart- preferredSeat
self.previousSeat- previousCart
self.previousCart- previousSeat
self.previousSeat- playClick
No
- Event
While the Left Mouse Button Is Held- Mode
Released
$detailsOpen- weight
self- number
self.weightNumber- rackX
self.rackX- rackY
self.rackY- height
self.weightHeight- destination
self.previousCart- preferredSeat
self.previousSeat- previousCart
self.previousCart- previousSeat
self.previousSeat- playClick
No
- Event
While the Left Mouse Button Is Held- Mode
Released
Before moving on, compare all six recovery Events with this panel. There are two Return routes: the Command and the Custom Event. Help also needs its Details Open condition. Check their destination: Released.
4.6.7 One Kilogram Weight
The small weight gets a resource of its own. Keep the Logic attachment order on the card: the copies need their identities and press listeners before the hand can use them.
Shared Per Instance names across those attachments refer to this same running weight. Leave the starting values alone.
Select Move Selected Weight in this Object’s Logic workspace. Under Properties > Components, choose Workshop weight-1 for Sprite (Sprite). Repeat on Select Workshop Weight. That name identifies the attached Sprite component using this picture; you are connecting the component, rather than choosing artwork again. Repeat both bindings with each later weight’s own named component.
X 0.5, Y 0.86Workshop weight-1X 0.5, Y 0.8Workshop weight-1Workshop weight-14.6.8 Two Kilogram Weight
The next denomination is taller, but it uses the same behavior. Make Two Kilogram Weight from its card. The pattern row supplies its mass; the size of the picture does not calculate it.
X 0.625, Y 0.956.04Workshop weight-2X 0.625, Y 0.95Workshop weight-2Workshop weight-24.6.9 Five Kilogram Weight
The broad weight uses those same attachments again. Match its picture and size, then leave placement to the pattern. These are resources for copies, rather than individual weights to drag into Lab.
X 0.75, Y 1.16.08Workshop weight-5X 0.75, Y 1.1Workshop weight-5Workshop weight-5The weights have behavior. The rack still needs its maker.
4.7 Fill the Rack
The bench can now create the arrangement described by its tables.
4.7.1 Prepare Workshop Weights
The carts must register before the first weights arrive. Prepare Workshop Weights waits for its one-shot timer, then walks the pattern rows and creates their stacks. The table does the arranging.
Before creating a copy, the Logic supplies its mass and rack coordinates. Its Create Event registers the copy. A successfully created starting load can then attach to Copper.
The final weightsPrepared message tells both carts to calculate their native masses. Load restores the existing copies, including the saved identities that link them to their table rows. Keep the timer and creation work in their separate Events.
Starts the workshop weight pattern once after the placed exact carts have registered. Saved generated weights are restored by native Load, so Load never generates a second set.
Any Object- Timer Name
"weightPatternReady"- Repeat Every (s)
0.02- Ring Once
Yes
- Action Name
seatSettings- Table
Cart Load Settings- Row
"cart"
seatSettings.value- Table
Cart Load Settings- Row
"cart"- Column
"capacity"- Value
round(data("Cart Load Settings","cart","capacity"))
data("Cart Load Settings","cart","capacity") < 1- Table
Cart Load Settings- Row
"cart"- Column
"capacity"- Value
1
data("Cart Load Settings","cart","capacity") > 4- Table
Cart Load Settings- Row
"cart"- Column
"capacity"- Value
4
- Action Name
addedRow- Table
Cart Load Settings- Row
"cart"- Values > capacity
4- Values > firstSeatX
-1.125- Values > spacing
0.75- Values > cartTop
0.35- Values > dropHalfWidth
2.1- Values > dropHalfHeight
1.3- Values > dropRise
0.7
- Name
seatsUsed- Value
0
- Table
Weight Pattern- Variable Name
"denomination"
- Name
copyCount- Value
round(denomination.copies)
copyCount < 0- Variable
copyCount- Value
0
copyCount > @self.stackCopyLimit- Variable
copyCount- Value
@self.stackCopyLimit
- Table
Weight Pattern- Row
denomination.index- Column
"copies"- Value
copyCount
- Name
placedCount- Value
0
- Name
loadCount- Value
round(denomination.initialCopper)
loadCount < 0- Variable
loadCount- Value
0
loadCount > copyCount- Variable
loadCount- Value
copyCount
loadCount > data("Cart Load Settings","cart","capacity") - seatsUsed- Variable
loadCount- Value
data("Cart Load Settings","cart","capacity") - seatsUsed
denomination.mass <= 0- Table
Weight Pattern- Row
denomination.index- Column
"mass"- Value
1
denomination.copiestimes- Variable
$preparingWeightMass- Value
denomination.mass
- Variable
$preparingRackX- Value
denomination.rackX
- Variable
$preparingRackY- Value
denomination.rackY + (copyCount - 1 - local.index) * denomination.stackRise
- Variable
$preparingHeight- Value
denomination.height
- Object to Create
denomination.object- X
denomination.rackX- Y
denomination.rackY + (copyCount - 1 - local.index) * denomination.stackRise- Variable Name
newWeight
- Action Name
weightExists- Applies to
newWeight
weightExists.value && local.index < loadCount && $copperCart.count == 1- Applies to
newWeight- Parent
$copperCart- Keep World Position
No- Local X
data("Cart Load Settings","cart","firstSeatX") + seatsUsed * data("Cart Load Settings","cart","spacing")- Local Y
data("Cart Load Settings","cart","cartTop") + denomination.height / 2- Destroy with Parent
No
- Table
Workshop Weights- Row
"Weight " + $weightCount- Column
"cart"- Value
2
- Table
Workshop Weights- Row
"Weight " + $weightCount- Column
"seat"- Value
seatsUsed + 1
- Variable
seatsUsed- Operator
+=- Value
1
- Variable
placedCount- Operator
+=- Value
1
weightExists.value == false- Variable
$pushStatus- Value
'A weight is unavailable; check Weight Pattern.'
- Table
Weight Pattern- Row
denomination.index- Column
"initialCopper"- Value
placedCount
- Custom Event
weightsPrepared
4.7.2 Choose Next Weight
Pressing the next-weight key should move the selection through the weights that remain. Choose Next Weight walks their registry, remembering the next identity and the first identity for wrapping.
Variable Name names the current row. The temporary firstNumber and nextNumber values are declared before that loop, so its nested changes can choose them.
N selects the next remaining generated weight in creation order using Workshop Weights, wrapping at the end. Destroy removes a weight from the list; Load restores it. An empty bench gives clear feedback.
Any Object- Name
nextNumber- Value
0
- Name
firstNumber- Value
0
- Table
Workshop Weights- Variable Name
"weight"
- Name
candidateNumber- Value
weight.number
firstNumber == 0 || candidateNumber < firstNumber- Variable
firstNumber- Value
candidateNumber
candidateNumber > $selectedWeightNumbernextNumber == 0 || candidateNumber < nextNumber- Variable
nextNumber- Value
candidateNumber
nextNumber > 0- Variable
$selectedWeightNumber- Value
nextNumber
- Variable
$selectedWeightNumber- Value
firstNumber
firstNumber == 0- Variable
$pushStatus- Value
'No workshop weights remain.'
4.7.3 Game Rules
Game Rules gains the rack maker and keyboard selection. Its card also names the ticket Components you will make below. Add the two weight attachments now, keeping their shown order; return for the ticket attachments when they exist.
The placed copies can finally make their weights. Run Lab here and look for the rack and Copper’s restored load. Try N and a load key. This checkpoint uses the keyboard. Update the press helper next before testing weight dragging.
4.7.4 Cart Press Input
The press helper now recognizes a weight above its cart. Replace its supplied source file. Keep the wiring as it was when you connected the earlier controls. Native Logic still owns movement and mass.
push-lab-cart-press-input-ch02.js2.8 KBpush-lab-cart-press-input-ch03.js2.9 KBcartPressCartPressInput4.7.5 Place Cart on Rail
A cart should wait while your hand is carrying a weight. The added guard in Place Cart on Rail checks that the weight hand is clear. Its other Events still perform the dragging and cleanup that you built for the carts earlier.
Place one exact cart along its rail with the pointer, release it from rest, or return it home.
Any ObjectCollider- Timer Name
"cartInputReady"- Repeat Every (s)
0.02- Ring Once
Yes
- Script
Capture Cart Press
self.pressSelected == trueself.pressSelected == true && dataFind("Workshop Weights","cart",-1) < 0- X Velocity
0- Y Velocity
0
- Component Value
self.collider.fixed- Value
true
- Event
While the Left Mouse Button Is Held- Mode
Holding
- Variable
handX- Value
valueMember(event.position,"x") - self.grabOffset
- X
clamp(self.handX, @self.leftLimit, @self.rightLimit)- Y
@self.laneY
- cart
self
- Event
While the Left Mouse Button Is Held- Mode
Released
- cart
self
- Event
While the Left Mouse Button Is Held- Mode
Released
- cart
self
- Event
While the Left Mouse Button Is Held- Mode
Released
- cart
self
- Event
While the Left Mouse Button Is Held- Mode
Released
- Script
Capture Cart Press
- cart
self- homeX
@self.homeX- laneY
@self.laneY
- Event
While the Left Mouse Button Is Held- Mode
Released
- cart
self- homeX
@self.homeX- laneY
@self.laneY
- Event
While the Left Mouse Button Is Held- Mode
Released
$detailsOpen- cart
self
- Event
While the Left Mouse Button Is Held- Mode
Released
The same bench can now carry different loads.
4.8 Keep the Push After It Ends
An observation needs a longer life than the motion it measured.
4.8.1 Validate Inspected Push
An old ticket may point at a row the history no longer retains. Check the row before keeping the view. A missing row returns the sheet to latest readings.
It changes the view alone. It never invents an empty measurement.
Returns to the latest reading if the inspected historical row was removed or trimmed.
$inspectedPush > 0- Action Name
inspectedRow- Table
Push Readings- Row
"Push " + $inspectedPush
inspectedRow.value == false- Variable
$inspectedPush- Value
0
4.8.2 Record Pair Reading
A push finishes measuring both carts, then its record goes into Push Readings. Record Pair Reading keeps that pair together under its batch identity. Extra old rows leave from the beginning.
The limit comes from readingCapacity. Its next accepted push applies an edit to that capacity. The final validation keeps an inspected ticket from pointing beyond the retained history.
Records one complete pair measurement, trims the oldest excess rows and reports how many are kept. Project Global readingCapacity (default 40) takes effect on the next accepted push.
$readingCapacity < 1- Variable
$readingCapacity- Value
1
- Variable
$readingCapacity- Value
round($readingCapacity)
- Action Name
addedRow- Table
Push Readings- Row
"Push " + $pushBatch- Values > batch
$pushBatch- Values > blueMass
$blueMass- Values > blueAppliedImpulse
$blueAppliedImpulse- Values > blueBefore
$blueBefore- Values > blueAfter
$blueAfter- Values > blueDelta
$blueDelta- Values > copperMass
$copperMass- Values > copperAppliedImpulse
$copperAppliedImpulse- Values > copperBefore
$copperBefore- Values > copperAfter
$copperAfter- Values > copperDelta
$copperDelta
dataCount("Push Readings") > $readingCapacitydataCount("Push Readings") - $readingCapacitytimes- Action Name
addedRow2- Operation
Remove- Table
Push Readings- Row
0
- Variable
$pushStatus- Value
"Impulse " + $pushBatch + " recorded (" + dataCount("Push Readings") + " kept)."
4.8.3 Push Workshop Pair
A held weight joins the held carts as a reason to refuse a push. Add the new guard and validity changes. Both carts still receive their impulses as one accepted pair.
Record Pair Reading waits until the batch has advanced and both measured results are ready to store. That order gives the stored row the identity of this push, with both immediate measurements already complete.
Pushes the exact workshop pair once only when both hands are clear. A held cart rejects the whole push.
param:blue.count == 1 && param:copper.count == 1param:blue.collider.fixed == false && param:copper.collider.fixed == false && dataFind("Workshop Weights","cart",-1) < 0compareparam:blue.collider.fixed == false && param:copper.collider.fixed == false- Action Name
bluePush- cart
blue- impulse
blueImpulse
- Variable
$blueMass- Value
bluePush.mass
- Variable
$blueBefore- Value
bluePush.before
- Variable
$blueAfter- Value
bluePush.after
- Variable
$blueAppliedImpulse- Value
blueImpulse
- Variable
$blueDelta- Value
$blueAfter - $blueBefore
- Action Name
copperPush- cart
copper- impulse
copperImpulse
- Variable
$copperMass- Value
copperPush.mass
- Variable
$copperBefore- Value
copperPush.before
- Variable
$copperAfter- Value
copperPush.after
- Variable
$copperAppliedImpulse- Value
copperImpulse
- Variable
$copperDelta- Value
$copperAfter - $copperBefore
- Variable
$blueReadingValid- Value
true
- Variable
$copperReadingValid- Value
true
- Variable
$pushBatch- Operator
+=- Value
1
- Variable
$pushStatus- Value
'Impulse applied to both carts.'
- accepted
Yes
- Variable
$pushStatus- Value
'Release the apparatus before pushing.'- Value
'Release both carts before pushing.'
- Variable
$pushStatus- Value
'Both workshop carts are needed for Push Both.'
The observation can stay after the carts stop.
4.9 Watch Current Motion
The needles will watch the carts continuously, including the moment their bodies meet the end stops.
4.9.1 Choose Gauge Calibration
A fast cart can outgrow a small scale. Choose Gauge Calibration finds a readable range with room above the speed. Its marks follow a growing pattern: 1, 2, 5, 10, 20, 50. The pattern repeats at ten times the size, with a wanted range of 3 moving up to 5. The logarithm finds the group. Later comparisons choose its mark.
This chosen range describes the face, keeping its markings readable as the measured speed grows. Changing it applies no force to the cart.
Chooses a signed velocity scale from the readable 1, 2, 5 sequence, with headroom above the measured speed. All inputs and the result are m/s except the headroom ratio.
- Name
wantedRange- Value
max(0.5, minimumRange, abs(speed) * max(1, headroom))
- Name
decade- Value
pow(10, floor(log(wantedRange) / log(10)))
- Name
calibration- Value
decade
calibration < wantedRange - 0.000000001- Variable
calibration- Value
decade * 2
calibration < wantedRange - 0.000000001- Variable
calibration- Value
decade * 5
calibration < wantedRange - 0.000000001- Variable
calibration- Value
decade * 10
- Value
calibration
4.9.2 Calibrate Recorded Gauge
The gauge begins with a scale suited to the expected push. After an observation, Calibrate Recorded Gauge considers the measured velocities around that push instead. It returns the range the face will use.
The velocity readings keep their signs, which still say whether a cart was moving right or left. Calibration needs their size, so it considers absolute speed.
Chooses the scale for one real paired observation, or the selected starting impulse when no observation exists. This never changes motion or a measurement.
- Name
observedSpeed- Value
0
cartNumber == 1$pushBatch > 0- Variable
observedSpeed- Value
max(abs($blueBefore), abs($blueAfter))
- Variable
observedSpeed- Value
$impulseMagnitude / max(0.001, $blueCart.transform.mass)
$pushBatch > 0- Variable
observedSpeed- Value
max(abs($copperBefore), abs($copperAfter))
- Variable
observedSpeed- Value
$impulseMagnitude / max(0.001, $copperCart.transform.mass)
- Action Name
recordedScale- speed
observedSpeed- minimumRange
minimumRange- headroom
headroom
- Value
recordedScale.value
4.9.3 Update Velocity Gauge
A cart hits its stop. The needle returns toward the center, although the earlier paper still shows the push. Update Velocity Gauge reads current native velocity and points the needle at that value.
The range can expand if the motion needs more room. The Function returns that range for the next update, without altering history or driving the cart.
Updates one live velocity instrument from an exact native cart, preserving historical readings. Calibration and speed are m/s; needleSweep is degrees.
StructuraltransformTransformspriteSprite- Name
calibration- Value
oldRange
- Action Name
cartExists- Applies to
cart
cartExists.value- Applies to
gauge- Visible
$detailsOpen == false
calibration < minimumRange- Variable
calibration- Value
minimumRange
calibration <= 0- Variable
calibration- Value
1
abs(param:cart.transform.velocity.x) > calibration- Action Name
expandedScale- speed
param:cart.transform.velocity.x- minimumRange
minimumRange- headroom
headroom
- Variable
calibration- Value
expandedScale.value
- Component Value
gauge.sprite.transform.rotation.z- Value
-param:cart.transform.velocity.x / calibration * needleSweep
- Applies to
gauge- Visible
false
- Value
calibration
4.9.4 Follow Native Velocity
The instrument needs to watch continuously because the cart keeps moving. Follow Native Velocity watches on Step. Create and a changed push batch choose a scale around the observation.
Declare the Sprite reference before attaching this Logic. The Object’s attachment will connect that reference specifically to the separate Sprite named Velocity Needle. The fixed face will keep its own position.
Shows one exact workshop cart's signed native velocity on its stationary calibrated instrument face. Native Text shows live speed and scale values; only its named needle rotates. Reads continuously because native velocity changes continuously.
Any ObjectSprite@self.cartNumber == 1- Action Name
followBlueCalibration- cart
$blueCart- gauge
self- oldRange
self.gaugeRange- minimumRange
@self.minimumRange- needleSweep
@self.needleSweep- headroom
@self.scaleHeadroom
- Variable
gaugeRange- Value
followBlueCalibration.value
- Action Name
followCopperCalibration- cart
$copperCart- gauge
self- oldRange
self.gaugeRange- minimumRange
@self.minimumRange- needleSweep
@self.needleSweep- headroom
@self.scaleHeadroom
- Variable
gaugeRange- Value
followCopperCalibration.value
- Action Name
calibrateOpeningScale- cartNumber
@self.cartNumber- minimumRange
@self.minimumRange- headroom
@self.scaleHeadroom
- Variable
gaugeRange- Value
calibrateOpeningScale.value
- Action Name
calibratePushScale- cartNumber
@self.cartNumber- minimumRange
@self.minimumRange- headroom
@self.scaleHeadroom
- Variable
gaugeRange- Value
calibratePushScale.value
4.9.5 Blue Live Gauge
The dial face, digits and needle belong to one gauge Object. Only Velocity Needle should rotate. Add the named extra Sprite and Text components before choosing that binding.
In the Object’s Logic workspace, select Follow Native Velocity. Under Properties > Components, set Sprite (Sprite) to Velocity Needle, as this card shows. Keep the Object’s minimumRange value from its card rather than the Logic’s default.
The digits read Blue’s current native velocity. The marks read the saved gauge range, so their numbers agree with the needle’s scale.
X 0.8, Y 0.8Velocity CalibrationX 2.5, Y 2.5{instanceMember($blueCart,"transform.velocity.x"):.2}0.225#39291BCenterWrap to Width1.1X -0.6, Y 0.035Velocity NeedleVelocity NeedleWorkshop needle2X 0, Y 0X 0.175, Y 0.6Velocity Mark 1{round(instanceVar("self","gaugeRange") * -1 * 100) / 100}0.1875#39291BCenterWrap to WidthX -0.770361618981152, Y 0.3592255224795940.75Velocity Mark 2{round(instanceVar("self","gaugeRange") * -0.5 * 100) / 100}0.1875#39291BCenterWrap to WidthX -0.4567046670948, Y 0.7168827289409530.75Velocity Mark 3{round(instanceVar("self","gaugeRange") * 0 * 100) / 100}0.1875#39291BCenterWrap to WidthX 0, Y 0.850.75Velocity Mark 4{round(instanceVar("self","gaugeRange") * 0.5 * 100) / 100}0.1875#39291BCenterWrap to WidthX 0.4567046670948, Y 0.7168827289409530.75Velocity Mark 5{round(instanceVar("self","gaugeRange") * 1 * 100) / 100}0.1875#39291BCenterWrap to WidthX 0.770361618981152, Y 0.3592255224795940.75Velocity Unitsm/s0.175#39291BCenterWrap to WidthX 0.875, Y 0.0350.6NumberConstant1NumberConstant0.5NumberConstant654.9.6 Copper Live Gauge
Copper gets its own stationary face and moving needle. Use the same attachment, with Copper’s cartNumber and the same named needle binding. This instrument watches the lower cart. Its marks use Copper’s saved range instead of Blue’s; the needle binding remains Velocity Needle.
Place both gauges using their setup rows. Run a shared push, then wait for Blue to reach its stop. Its live digits settle while Copper can still move.
X 0.8, Y 0.8Velocity CalibrationX 2.5, Y 2.5{instanceMember($copperCart,"transform.velocity.x"):.2}0.225#39291BCenterWrap to Width1.1X -0.6, Y 0.035Velocity NeedleVelocity NeedleWorkshop needle2X 0, Y 0X 0.175, Y 0.6Velocity Mark 1{round(instanceVar("self","gaugeRange") * -1 * 100) / 100}0.1875#39291BCenterWrap to WidthX -0.770361618981152, Y 0.3592255224795940.75Velocity Mark 2{round(instanceVar("self","gaugeRange") * -0.5 * 100) / 100}0.1875#39291BCenterWrap to WidthX -0.4567046670948, Y 0.7168827289409530.75Velocity Mark 3{round(instanceVar("self","gaugeRange") * 0 * 100) / 100}0.1875#39291BCenterWrap to WidthX 0, Y 0.850.75Velocity Mark 4{round(instanceVar("self","gaugeRange") * 0.5 * 100) / 100}0.1875#39291BCenterWrap to WidthX 0.4567046670948, Y 0.7168827289409530.75Velocity Mark 5{round(instanceVar("self","gaugeRange") * 1 * 100) / 100}0.1875#39291BCenterWrap to WidthX 0.770361618981152, Y 0.3592255224795940.75Velocity Unitsm/s0.175#39291BCenterWrap to WidthX 0.875, Y 0.0350.6NumberConstant2NumberConstant0.5NumberConstant65You can now watch the motion without opening the sheet or changing an earlier observation.
4.10 Put History on Paper
The printer and pinned papers keep the experiment’s earlier answers.
4.10.1 Inspect Recorded Push
Press an old paper and its observation should open larger. Inspect Recorded Push checks for that batch’s row, then opens the reading sheet with that identity selected. Motion continues underneath.
A missing or trimmed row leaves the view alone. A batch number identifies an observation even when earlier rows have gone.
Opens the retained push identified by its batch number. Missing or trimmed rows leave the current sheet unchanged.
- Action Name
recordedPush- Table
Push Readings- Row
"Push " + batch
recordedPush.value- Variable
$inspectedPush- Value
batch
- Variable
$detailsOpen- Value
true
4.10.2 Read Latest Print
The printer always shows the latest retained observation, whichever old ticket you inspect. Read Latest Print hides its paper until a row exists and lets an exact press open the larger view.
A changed batch or table refreshes visibility. Opening Help covers the bench paper, while Load restores whether there is something to print.
Prints the latest retained numerical measurement on the printer paper; an exact press opens its optional enlarged reading.
Any Object- Visible
$detailsOpen == false && dataCount("Push Readings") > 0
- Visible
$detailsOpen == false && dataCount("Push Readings") > 0
- Visible
$detailsOpen == false && dataCount("Push Readings") > 0
- Visible
$detailsOpen == false && dataCount("Push Readings") > 0
self.pressSelected == true$detailsOpen == false && dataCount("Push Readings") > 0- batch
data("Push Readings",dataCount("Push Readings") - 1,"batch")
- Visible
$detailsOpen == false && dataCount("Push Readings") > 0
4.10.3 Printer Reading
The printer’s compact columns put Blue beside Copper. Its Text template reads the last retained row directly, so later cart motion does not rewrite those values. Build its listener and print attachments in the shown order.
Keep each line break in the template. The paper is small, with the larger inspection available on a press.
X 2.8, Y 2.9PUSH {data("Push Readings",dataCount("Push Readings") - 1,"batch")} [new line] B C [new line] kg {data("Push Readings",dataCount("Push Readings") - 1,"blueMass"):.2} {data("Push Readings",dataCount("Push Readings") - 1,"copperMass"):.2} [new line] J {data("Push Readings",dataCount("Push Readings") - 1,"blueAppliedImpulse"):.2} {data("Push Readings",dataCount("Push Readings") - 1,"copperAppliedImpulse"):.2} [new line] v0 {data("Push Readings",dataCount("Push Readings") - 1,"blueBefore"):.2} {data("Push Readings",dataCount("Push Readings") - 1,"copperBefore"):.2} [new line] v1 {data("Push Readings",dataCount("Push Readings") - 1,"blueAfter"):.2} {data("Push Readings",dataCount("Push Readings") - 1,"copperAfter"):.2} [new line] dv {data("Push Readings",dataCount("Push Readings") - 1,"blueDelta"):.2} {data("Push Readings",dataCount("Push Readings") - 1,"copperDelta"):.2} [new line] m/s0.225#352C24CenterWrap to Width2.7-1Printed Observation PaperX -0.075, Y 1.35X 2.65, Y 2.954.10.4 Read Pinned Ticket
Each pinned paper needs to know which recent observation it represents. Each copy receives its slot at creation, before any later input asks it to inspect a row. That slot follows the retained end of the history.
Row and view changes refresh visibility. Its press opens the batch currently printed on that paper. The printer keeps its own latest row, even while you examine this older piece of paper.
Prints one of the three newest immutable numerical measurements on its pinned paper; an exact press opens its optional enlarged reading.
Any Object- Variable
ticketSlot- Value
$preparingTicketSlot
- Depth Amount
25
- Visible
$detailsOpen == false && self.ticketSlot <= dataCount("Push Readings")
- Visible
$detailsOpen == false && self.ticketSlot <= dataCount("Push Readings")
- Visible
$detailsOpen == false && self.ticketSlot <= dataCount("Push Readings")
- Visible
$detailsOpen == false && self.ticketSlot <= dataCount("Push Readings")
self.pressSelected == true$detailsOpen == false && self.ticketSlot <= dataCount("Push Readings")- batch
data("Push Readings",max(0,dataCount("Push Readings") - 3) + self.ticketSlot - 1,"batch")
- Visible
$detailsOpen == false && self.ticketSlot <= dataCount("Push Readings")
4.10.5 Reading Ticket
A ticket combines paper, printed values and a listener. Its Text template uses the saved ticketSlot to find the matching recent row. Keep the template and its line breaks whole.
With five retained rows, the three slots display the last three observations in order. Slot one starts at row index two, counting from zero. The next two slots follow it. After another push, the window moves forward together.
Make the Object without placing copies; the pattern will arrange them in their board positions. The next Component will create the regularly spaced papers.
X 1.7, Y 2.2#{data("Push Readings",max(0,dataCount("Push Readings") - 3) + instanceVar("self","ticketSlot") - 1,"batch")} [new line] B {data("Push Readings",max(0,dataCount("Push Readings") - 3) + instanceVar("self","ticketSlot") - 1,"blueMass"):.2} kg [new line] {data("Push Readings",max(0,dataCount("Push Readings") - 3) + instanceVar("self","ticketSlot") - 1,"blueAfter"):.2} m/s [new line] C {data("Push Readings",max(0,dataCount("Push Readings") - 3) + instanceVar("self","ticketSlot") - 1,"copperMass"):.2} kg [new line] {data("Push Readings",max(0,dataCount("Push Readings") - 3) + instanceVar("self","ticketSlot") - 1,"copperAfter"):.2} m/s [new line] dv {data("Push Readings",max(0,dataCount("Push Readings") - 3) + instanceVar("self","ticketSlot") - 1,"blueDelta"):.2} [new line] / {data("Push Readings",max(0,dataCount("Push Readings") - 3) + instanceVar("self","ticketSlot") - 1,"copperDelta"):.2}0.175#352C24CenterWrap to Width1.44.10.6 Prepare Reading Tickets
The board gains its papers. Prepare Reading Tickets supplies each slot before creating its copy, as the weight maker did with each weight’s starting information.
Create runs that pattern once. Native Load restores the existing tickets and their identities.
Creates the three regularly spaced paper controls once. Native Load restores them and their saved slot identities without creating more.
Any Object3times- Variable
$preparingTicketSlot- Value
local.index + 1
- Object to Create
data("Reading Ticket Settings","papers","object")- X
data("Reading Ticket Settings","papers","firstX") + data("Reading Ticket Settings","papers","spacing") * local.index- Y
data("Reading Ticket Settings","papers","paperY")
4.10.7 Choose Historical Ticket
The keyboard can inspect the papers too. Choose Historical Ticket opens the first recent ticket, then advances through the pinned observations on later requests. An empty history has nothing to open.
Its table-change and Load Events validate the selected batch. A removed row cannot leave a fabricated reading on the sheet.
T opens the oldest pinned reading, then cycles through the retained three. H closes the sheet; reopening Help returns to the latest page.
Any ObjectdataCount("Push Readings") > 0- Name
ticketIndex- Value
dataFind("Push Readings","batch",$inspectedPush) + 1
ticketIndex < max(0,dataCount("Push Readings") - 3) || ticketIndex >= dataCount("Push Readings")- Variable
ticketIndex- Value
max(0,dataCount("Push Readings") - 3)
- batch
data("Push Readings",ticketIndex,"batch")
Return to Game Rules now. Add Prepare Reading Tickets and Choose Historical Ticket in the remaining positions on its card.
4.11 Separate the Sheet Views
The sheet can show either the latest comparison or an older observation.
4.11.1 Toggle Workshop Readings
Reopening Help returns to latest readings. The change to Toggle Workshop Readings clears that historical choice when the sheet opens. Closing still leaves the experiment running.
Toggles only the saved reading-sheet view. The experiment continues unchanged.
$detailsOpen- Variable
$detailsOpen- Value
false
- Variable
$detailsOpen- Value
true
$detailsOpen- Variable
$inspectedPush- Value
0
4.11.2 Set Reading Visibility
Moving a weight makes the affected latest comparison stale. Set Reading Visibility now receives a validity value as well as the caption. A genuine zero impulse remains a measurement.
The latest caption also waits while the historical page is selected. An old observation has its own captions below.
Shows a genuine measured push only in the optional reading sheet; zero impulse is a measurement, the opening workshop is not.
$detailsOpen && $pushBatch > 0$inspectedPush == 0 && ($detailsOpen && $pushBatch > 0 && valid)- Applies to
caption- Visible
true
- Applies to
caption- Visible
false
4.11.3 Show Measured Reading
Blue and Copper can lose validity separately. Show Measured Reading uses its holding Object’s readingCartNumber to choose the matching validity value. The new Variable Changed Events refresh that choice when a load changes.
Keep the added inspectedPush Event too. It lets the latest captions return when you leave an older observation.
Refreshes measurement visibility when the workshop opens, captures a push or finishes Load.
Any Object- caption
self- valid
(@self.readingCartNumber == 1 && $blueReadingValid) || (@self.readingCartNumber == 2 && $copperReadingValid)
- caption
self- valid
(@self.readingCartNumber == 1 && $blueReadingValid) || (@self.readingCartNumber == 2 && $copperReadingValid)
- caption
self- valid
(@self.readingCartNumber == 1 && $blueReadingValid) || (@self.readingCartNumber == 2 && $copperReadingValid)
- caption
self- valid
(@self.readingCartNumber == 1 && $blueReadingValid) || (@self.readingCartNumber == 2 && $copperReadingValid)
- caption
self- valid
(@self.readingCartNumber == 1 && $blueReadingValid) || (@self.readingCartNumber == 2 && $copperReadingValid)
- caption
self- valid
(@self.readingCartNumber == 1 && $blueReadingValid) || (@self.readingCartNumber == 2 && $copperReadingValid)
- caption
self- valid
(@self.readingCartNumber == 1 && $blueReadingValid) || (@self.readingCartNumber == 2 && $copperReadingValid)
4.11.4 Show Reading Page
The pages need different words. Show Reading Page compares the selected view with its historyPage value. The appropriate captions appear together.
Set historyPage on each holding Object after attachment. Yes chooses history. No chooses the current instructions and status, rather than an earlier observation’s page.
Shows a latest-page or historical-page caption inside the optional reading sheet. historyPage chooses the historical page.
Any Object- Visible
$detailsOpen && (($inspectedPush > 0) == @self.historyPage)
- Visible
$detailsOpen && (($inspectedPush > 0) == @self.historyPage)
- Visible
$detailsOpen && (($inspectedPush > 0) == @self.historyPage)
- Visible
$detailsOpen && (($inspectedPush > 0) == @self.historyPage)
4.11.5 Blue Push Reading
Blue’s latest caption gains its own cart identity. Match its Text. Blue keeps the default readingCartNumber value of one from Show Measured Reading; this Object card needs no override. The validity check now knows which cart this caption describes.
X 0.8, Y 0.8Blue: {$blueMass} kg | J {$blueAppliedImpulse} N s [new line] {round($blueBefore * 100) / 100}->{round($blueAfter * 100) / 100}; change:{round($blueDelta * 100) / 100}m/sBlue: {$blueMass} kg | J {$blueAppliedImpulse} N s [new line] v {round($blueBefore * 100) / 100} -> {round($blueAfter * 100) / 100} m/s | change {round($blueDelta * 100) / 100} m/s0.8Medium-0.032CenterWrap to Width224.11.6 Copper Push Reading
Give Copper the same connection to its own identity, keeping that value separate from Blue’s. Keep its own identity rather than Blue’s default, so a changed Copper load invalidates the correct latest comparison.
X 0.8, Y 0.8Copper: {$copperMass} kg | J {$copperAppliedImpulse} N s [new line] {round($copperBefore * 100) / 100}->{round($copperAfter * 100) / 100}; change:{round($copperDelta * 100) / 100}m/sCopper: {$copperMass} kg | J {$copperAppliedImpulse} N s [new line] v {round($copperBefore * 100) / 100} -> {round($copperAfter * 100) / 100} m/s | change {round($copperDelta * 100) / 100} m/s0.8Medium-0.032CenterWrap to Width2224.11.7 Workshop Instructions
The reminder now includes weight placement and ticket inspection. Replace its marked Text and change the attachment to Show Reading Page. This reminder belongs on the latest page.
Replace [new line] with a break. The historical view has a shorter reminder of its own, with the controls for inspecting earlier observations.
X 0.8, Y 0.8Drag carts or weights; release from rest. [new line] Space: Push Home: Return Arrows: J / sign [new line] N: Weight 1 / 2: Load cart Delete: Rack [new line] End: max 0: zero T: tickets H: closeDrag carts; release from rest. [new line] Space: Push Home: Return Arrows: J / sign [new line] End: max 0: zero [new line] Experiment continues. H: close0.8MediumCenterWrap to Width224.11.8 Push Status
Keep current feedback beside its controls, where a refused request can explain what needs changing. Push Status also changes to Show Reading Page, with historyPage No. An old paper describes its observation.
X 0.8, Y 0.8{$pushStatus}0.8MediumCenterWrap to Width224.11.9 Historical Blue Reading
The enlarged Blue reading looks up the selected batch in Push Readings. Its values come from that retained row, even after Blue’s load or velocity changes. Give its page attachment historyPage Yes.
X 22, Y 3.3Blue: {data("Push Readings","Push " + $inspectedPush,"blueMass")} kg | J {round(data("Push Readings","Push " + $inspectedPush,"blueAppliedImpulse") * 100) / 100} N s [new line] {round(data("Push Readings","Push " + $inspectedPush,"blueBefore") * 100) / 100} -> {round(data("Push Readings","Push " + $inspectedPush,"blueAfter") * 100) / 100} m/s [new line] change: {round(data("Push Readings","Push " + $inspectedPush,"blueDelta") * 100) / 100} m/s0.8Medium#F4E8CFCenterWrap to Width22Yes4.11.10 Historical Copper Reading
Copper’s historical caption reads the same batch, with Copper’s recorded fields. Its setup row puts the caption below Blue’s, leaving space to compare the two recorded answers. The pair remains a comparison of one past push.
X 22, Y 3.3Copper: {data("Push Readings","Push " + $inspectedPush,"copperMass")} kg | J {round(data("Push Readings","Push " + $inspectedPush,"copperAppliedImpulse") * 100) / 100} N s [new line] {round(data("Push Readings","Push " + $inspectedPush,"copperBefore") * 100) / 100} -> {round(data("Push Readings","Push " + $inspectedPush,"copperAfter") * 100) / 100} m/s [new line] change: {round(data("Push Readings","Push " + $inspectedPush,"copperDelta") * 100) / 100} m/s0.8Medium#F4E8CFCenterWrap to Width22Yes4.11.11 History Note
The historical sheet names its batch and explains why its values stay still. History Note gives the reader a way back to recent observations and the latest page. Place it using the remaining setup row.
Next Impulse, Impulse Dial Face, Reading Sheet, Reading Sheet Title and Help Control keep last chapter’s card values. Their existing view attachments still serve the same bench and sheet.
X 22, Y 3.8PUSH #{$inspectedPush} / recorded snapshot [new line] Later changes do not rewrite this reading. [new line] T: next pinned ticket H: close [new line] Reopen Help for the latest reading.0.8Medium#F4E8CFCenterWrap to Width22YesThe bench now shows motion and keeps its observations.
4.12 Play It!
Predict how adding a weight will change Blue’s answer to the same impulse. Keep the chosen impulse unchanged while testing that prediction.
- Run Lab. Blue begins without a load; Copper carries its starting weights. A shared +4 N s push gives Blue a change of +4 m/s and Copper +0.4 m/s.
- Return the carts with Home. Move a 1 kg weight from the rack onto Blue, then push again. Blue’s native mass is now 2 kg and its change is +2 m/s. Copper’s response stays +0.4 m/s.
- Select a weight with N. Use 1 or 2 to load the selected weight, then Delete to return it to the rack. The selected weight changes tint.
- Try a full cart. A refused extra weight keeps its previous place. Release it elsewhere on the bench to return it to its rack position.
- Push, then change Blue’s load while Copper is still moving. Blue stops; Copper keeps moving. Open Help and look for Blue’s latest comparison to disappear until another push.
- Watch a gauge while its cart reaches a stop. Its live digits reach zero. The printer keeps the earlier mass, impulse and before-and-after velocities.
- Make several accepted pushes. The pinned papers follow the newest observations; the printer stays on the latest one. Inspect an older ticket with T.
- Close with H, change the load, then inspect that retained ticket again. Its old mass and velocities remain unchanged. Close the sheet and reopen Help when you want to see the latest comparison again.
- Hold a weight and press Space. The whole pair push is refused. Let go, then push again; the bench is ready for another observation.
4.13 How It Works
The pattern creates weights. Each weight records its place. A transfer changes the load, and each affected cart updates its native mass.
The push still measures velocity immediately before and after the impulse. That change equals impulse divided by the actual native mass measured inside the same call. The gauges watch later motion; the history keeps that earlier measurement.
Changing the apparatus starts a fresh comparison for the affected cart. Keeping the old observation separate lets you compare experiments without confusing them with the bench’s current state.
4.14 If It Didn’t Work
The rack stays empty? Look at Game Rules first. Prepare Workshop Weights needs its attachment, the named weight resources and matching object text in Weight Pattern. Restore one missing connection, then run Lab again.
Copper has two loads drawn in the same place? Compare the extra Sprite names with the removed components on Copper Cart’s marked Object card. The earlier painted load comes out when movable weights take over. Keep the generated copies and remove those old extra pictures.
A weight moves but mass stays unchanged? Follow its registry row and cartLoadChanged. Change Native Cart Load needs to be attached to each cart. Restore that listener and try the transfer again.
The whole gauge face turns? Select Follow Native Velocity on that gauge’s Logic workspace. Its Components Sprite picker needs Velocity Needle. Change that binding and the face should stay still while the needle turns.
A row expression names an unknown weight? Compare its For Each Row Variable Name with the expression below it. The row’s name belongs inside that loop. A temporary Declare Variable must also come before the Actions that change it.
An old paper changes when the cart stops? Check where its Text reads. Printer Reading and Reading Ticket use Push Readings; the gauges use native velocity. Restore that distinction and the paper can keep its moment.
The latest caption remains after its load changes? Compare that caption’s readingCartNumber with its cart, then check the added validity Events. A new accepted push restores a valid comparison.
The sheet opens on the wrong page? Follow inspectedPush through the view Functions, then compare the page choice on each caption Object. The historical captions use historyPage Yes; the current instructions and status use No. Restore the mismatched holder value.
4.15 Challenge
Choose two load arrangements that you think will give the same change in velocity with different impulses. Make each experiment and keep its paper. Use the historical view to compare your prediction with the recorded masses and changes.
The bench keeps what you observed. Next, you can ask it for a particular result.
