Skip to content

Proximity Sensors

See this page in the Vizard Documentation for more information on Proximity Sensors

https://docs.worldviz.com/vizard/latest/#vizproximity.htm%3FTocPath%3DReference%7CProximity%20Sensors%7C_____1 

Use the vizproximity library to detect proximity between Vizard objects and trigger events whenever a target enters or exits a sensor’s range. The library provides three main classes:

  • Sensor – Monitors an area or volume in your scene.
  • Target – Represents any linkable object (e.g., node, view, tracker, bone) whose position is checked against the sensor.
  • Manager – Oversees a collection of sensors and targets, automatically triggering events when a target crosses a sensor boundary.

For a quick introduction, see the example script ExampleScripts/Proximity Sensors/proximitySensors_example.py.
If you’re using SightLab 2.1.1 or higher, check out proximitySensors_Areas.py and proximitySensors_Areas2.py (the same maze areas, plus the ball from the first example). These scripts demonstrate how you can place a proximity_area.osgb object (from sightlab_resources/objects) directly in a 3D scene, then move, rotate, or scale it. The object automatically updates its sensor area based on the transformations you apply.

All three scripts record what happens during a trial: each entry into and exit from an area, and each touch of the ball, is written to the trial data’s flag column, and the scripts with the ball add the time to the first touch and the hand that made it to the experiment summary. Press m to show or hide the sensors’ outlines.

import vizshape
import vizproximity
import sightlab_utils.sightlab as sl
from sightlab_utils.settings import *

TRIAL_COUNT = 3
BALL_RADIUS = 0.1
BEEP = 'sightlab_resources/audio/beep.wav'
START_TEXT = 'Touch the blue ball with either hand.\n\nPress Space to start.'
END_TEXT = 'Done. Thank you!'

sightlab = sl.SightLab(gui = False)
sightlab.setTrialCount(TRIAL_COUNT)

env = vizfx.addChild('sightlab_resources/environments/Business_Office.OSGB')
sightlab.setEnvironment(env)

# Create objects
ball = vizshape.addSphere(radius = BALL_RADIUS)
ball.color(viz.BLUE)
ball.setPosition(0, 1.5, 2)
# Registered with SightLab, so the ball is in the gaze data and the replay
sightlab.addSceneObject('ball', ball, gaze = True)

# Load the beep now, so the first one plays without a delay
viz.playSound(BEEP, viz.SOUND_PRELOAD)

# The ball detects the hands
avatar = vizconnect.getAvatar()
rhand_target = vizproximity.Target(avatar.getAttachmentPoint('r_hand').getNode3d())
lhand_target = vizproximity.Target(avatar.getAttachmentPoint('l_hand').getNode3d())
HAND_NAMES = {rhand_target: 'right hand', lhand_target: 'left hand'}

handManager = vizproximity.Manager()
handManager.addTarget(rhand_target)
handManager.addTarget(lhand_target)

# A sensor the size of the ball. addBoundingSphereSensor(ball) would be 0.17 m: a
# sphere's bounding sphere is the one around its bounding box
BallSensor = handManager.addSensor(vizproximity.Sensor(vizproximity.Sphere(BALL_RADIUS), source = ball))

# The floor square detects the participant's viewpoint
target = vizproximity.Target(viz.MainView)
areaManager = vizproximity.Manager()
areaManager.addTarget(target)

# A 1 x 1 m square on the floor, ahead and to the left of the start, clear of the
# walk to the ball
FloorArea = areaManager.addSensor(vizproximity.Sensor(vizproximity.RectangleArea([1, 1], center = [-0.9, 1.0]), None))

# Proximity events are recorded only while a trial runs. Between trials SightLab
# has no trial data to write them to
trialRunning = False

def onTrialStart():
    global trialRunning
    trialRunning = True
    # Standing on the square already: there is no entry to record
    if FloorArea in areaManager.getActiveSensors(target):
        recordFlag('on floor area at trial start')

def onTrialEnd():
    global trialRunning
    trialRunning = False

vizact.addCallback(TRIAL_START, onTrialStart)
vizact.addCallback(TRIAL_END, onTrialEnd)

def recordFlag(text):
    """Write text to the trial data's flag column, on this frame."""
    if trialRunning:
        sightlab.setCustomTrialData(text)

# Register callbacks for proximity sensors
def EnterFloorArea(e):
    viz.playSound(BEEP)
    print('Entered floor area')
    recordFlag('entered floor area')

def ExitFloorArea(e):
    print('Exited floor area')
    recordFlag('exited floor area')
areaManager.onEnter(FloorArea, EnterFloorArea)
areaManager.onExit(FloorArea, ExitFloorArea)

def TouchBall(e):
    viz.playSound(BEEP)
    print('Ball touched with the', HAND_NAMES[e.target])
    recordFlag('ball touched with the ' + HAND_NAMES[e.target])
handManager.onEnter(BallSensor, TouchBall)

# Press "m" to show or hide the sensors' outlines ("d" would also strafe right on Desktop)
handManager.setDebug(True)
areaManager.setDebug(True)
vizact.onkeydown('m', handManager.setDebug, viz.TOGGLE)
vizact.onkeydown('m', areaManager.setDebug, viz.TOGGLE)

def sightLabExperiment():
    yield viztask.waitEvent(EXPERIMENT_START)
    for trial in range(1, TRIAL_COUNT + 1):
        yield sightlab.startTrial(trialLabel = 'Trial_{}'.format(trial), startTrialText = START_TEXT, textContinueKey = ' ')
        startTime = viz.tick()

        e = yield vizproximity.waitEnter(BallSensor, [rhand_target, lhand_target])
        print('hand sensor activated')
        # SightLab keeps these columns from trial to trial, so set them every trial
        sightlab.setExperimentSummaryData('Time to Touch Ball (secs)', round(viz.tick() - startTime, 3))
        sightlab.setExperimentSummaryData('Touched With', HAND_NAMES[e.target])

        yield viztask.waitKeyDown(' ')
        yield sightlab.endTrial(endExperimentText = END_TEXT)

viztask.schedule(sightlab.runExperiment)
viztask.schedule(sightLabExperiment)
viz.callback(viz.getEventID('ResetPosition'), sightlab.resetViewPoint)

Second Method (add proximity_area.osgb to a 3D scene in the scene editor, move, rotate and scale it how you want. To add multiple sensors add the same object and name the geode a different name (i.e. geode1, geode2, etc.). Can right click and choose "Rename".

import sightlab_utils.sightlab as sl
from sightlab_utils.settings import *
from sightlab_utils import proximityAreaPlacer

TRIAL_COUNT = 3
START_TEXT = 'Walk through the maze.\n\nPress Space to start the trial, and Space again to end it.'
END_TEXT = 'Done. Thank you!'

sightlab = sl.SightLab(gui = False)
sightlab.setTrialCount(TRIAL_COUNT)

env = vizfx.addChild("Resources/environment/maze.osgb")
sightlab.setEnvironment(env)

sensor1 = proximityAreaPlacer.placeProximity(env, 'proximity_geode1')
sensor2 = proximityAreaPlacer.placeProximity(env, 'proximity_geode2')
AREA_NAMES = {sensor1: 'proximity_geode1', sensor2: 'proximity_geode2'}

# Proximity events are recorded only while a trial runs. Between trials SightLab
# has no trial data to write them to
trialRunning = False

def onTrialStart():
    global trialRunning
    trialRunning = True
    # An area the participant is already standing in has no entry to record
    for sensor in proximityAreaPlacer.manager.getActiveSensors(proximityAreaPlacer.viewTarget):
        recordFlag('in ' + AREA_NAMES[sensor] + ' at trial start')

def onTrialEnd():
    global trialRunning
    trialRunning = False

vizact.addCallback(TRIAL_START, onTrialStart)
vizact.addCallback(TRIAL_END, onTrialEnd)

def recordFlag(text):
    """Write text to the trial data's flag column, on this frame."""
    if trialRunning:
        sightlab.setCustomTrialData(text)

# Register callbacks for proximity sensors
def EnterProximity1(e):
    print('Entered Sensor1')
    recordFlag('entered proximity_geode1')

def ExitProximity1(e):
    print('Exited Sensor1')
    recordFlag('exited proximity_geode1')
proximityAreaPlacer.manager.onEnter(sensor1, EnterProximity1)
proximityAreaPlacer.manager.onExit(sensor1, ExitProximity1)

def EnterProximity2(e):
    print('Entered Sensor2')
    recordFlag('entered proximity_geode2')

def ExitProximity2(e):
    print('Exited Sensor2')
    recordFlag('exited proximity_geode2')
proximityAreaPlacer.manager.onEnter(sensor2, EnterProximity2)
proximityAreaPlacer.manager.onExit(sensor2, ExitProximity2)

def sightLabExperiment():
    yield viztask.waitEvent(EXPERIMENT_START)
    for trial in range(1, TRIAL_COUNT + 1):
        yield sightlab.startTrial(trialLabel = 'Trial_{}'.format(trial), startTrialText = START_TEXT, textContinueKey = ' ')

        yield viztask.waitKeyDown(' ')
        yield sightlab.endTrial(endExperimentText = END_TEXT)

viztask.schedule(sightlab.runExperiment)
viztask.schedule(sightLabExperiment)
viz.callback(viz.getEventID('ResetPosition'), sightlab.resetViewPoint)

For more information see Vizard Documentation on Proximity Sensors