def set_clipboard(self, callback=None, object_type=None, scale=Scale(0.05, 0.05, 0.05), position=Position(0, 0, -CLIP_RADIUS/SCL_HUD), color=Color(255, 255, 255), url=None): if object_type: self.clipboard = Object( # show item to be created object_id=f"{self.camname}_clipboard", object_type=object_type, position=position, parent=self.hud.object_id, scale=Scale(scale.x/SCL_HUD, scale.y/SCL_HUD, scale.z/SCL_HUD), material=Material(color=color, transparent=True, opacity=0.4), url=url, clickable=True, evt_handler=callback) self.scene.add_object(self.clipboard) self.cliptarget = Circle( # add helper target object to find true origin object_id=f"{self.camname}_cliptarget", position=position, parent=self.hud.object_id, scale=Scale(0.005/SCL_HUD, 0.005/SCL_HUD, 0.005/SCL_HUD), material=Material(color=Color(255, 255, 255), transparent=True, opacity=0.4), clickable=True, evt_handler=callback) self.scene.add_object(self.cliptarget)
def init_origin(scene: Scene): """Origin object, construction cone, so user knows ARB is running.""" # TODO: migrate to shared-scene setting size = [0.2, 0.4, 0.2] scene.add_object(Cone( # 370mm x 370mm # 750mm object_id="arb-origin", material=Material( color=Color(255, 114, 33), transparent=True, opacity=0.5, shader="flat"), position=Position(0, size[1] / 2, 0), scale=Scale(size[0] / 2, size[1], size[2] / 2))) scene.add_object(Cone( object_id="arb-origin-hole", **{"material-extras": {"transparentOccluder": True}}, position=Position(0, size[1] - (size[1] / 2 / 15), 0), scale=Scale(size[0] / 15, size[1] / 10, size[2] / 15))) scene.add_object(Box( object_id="arb-origin-base", material=Material( color=Color(0, 0, 0), transparent=True, opacity=0.5, shader="flat"), position=Position(0, size[1] / 20, 0), scale=Scale(size[0], size[1] / 10, size[2])))
def update_dropdown(camname, objid, mode, options, row, callback): # show new dropdown if USERS[camname].panel[objid].active: followname = USERS[camname].follow.object_id maxwidth = min(len(options), 10) drop_button_offset = -math.floor(maxwidth / 2) for i, option in enumerate(options): if mode is Mode.COLOR: bcolor = Color(option) else: bcolor = arblib.CLR_SELECT dbutton = arblib.Button(scene, camname, mode, (i % maxwidth) + drop_button_offset, row, label=option, parent=followname, color=bcolor, drop=option, callback=callback) USERS[camname].dbuttons[dbutton.button.object_id] = dbutton if (i + 1) % maxwidth == 0: # next row if row < 0: row -= 1 else: row += 1 # make default selection if mode is Mode.COLOR: rcolor = Color(options[0]) else: rcolor = arblib.CLR_HUDTEXT USERS[camname].set_textright(options[0], color=rcolor) USERS[camname].target_style = options[0]
def temp_rot_marker(position, rotation): return Box(ttl=120, rotation=rotation, material=Material(color=Color(255, 255, 255)), position=position, scale=Scale(0.02, 0.01, 0.15), clickable=True)
def color_callback(_scene, event, msg): camname, objid, drop = handle_panel_event(event, dropdown=True) if not camname or not objid: return hcolor = drop color = Color(hcolor) USERS[camname].set_textright(hcolor, color=color) USERS[camname].target_style = hcolor
def do_wall_end(camname): # end (green) USERS[camname].wloc_end = USERS[camname].position USERS[camname].wrot_end = USERS[camname].rotation scene.add_object( arblib.temp_loc_marker(USERS[camname].wloc_end, Color(0, 255, 0))) scene.add_object( arblib.temp_rot_marker(USERS[camname].wloc_end, USERS[camname].wrot_end))
def do_wall_start(camname): # start (red) USERS[camname].wloc_start = USERS[camname].position USERS[camname].wrot_start = USERS[camname].rotation scene.add_object( arblib.temp_loc_marker(USERS[camname].wloc_start, Color(255, 0, 0))) scene.add_object( arblib.temp_rot_marker(USERS[camname].wloc_start, USERS[camname].wrot_start))
def set_lamp(self, enabled): if enabled: self.lamp = Light(object_id=f"{self.camname}_lamp", parent=self.hud.object_id, material=Material(color=Color(144, 144, 173)), type="point", intensity=0.75) self.scene.add_object(self.lamp) elif self.lamp: self.scene.delete_object(self.lamp)
def do_move_select(camname, object_id): obj = scene.get_persisted_obj(object_id) USERS[camname].target_id = object_id object_type = "box" if "object_type" in obj.data: object_type = obj.data.object_type scale = Scale() if "scale" in obj.data: scale = obj.data.scale color = Color() if "material" in obj.data and "color" in obj.data.material: color = obj.data.material.color url = None if "url" in obj.data: url = obj.data.url USERS[camname].set_clipboard( callback=clipboard_callback, object_type=object_type, scale=scale, color=color, url=url, )
from arena import (Box, Circle, Color, Cone, Cylinder, Dodecahedron, Icosahedron, Light, Material, Object, Octahedron, Plane, Position, Ring, Rotation, Scale, Scene, Sphere, Tetrahedron, Text, Torus, TorusKnot, Triangle) CLICKLINE_LEN_OBJ = 0.5 # meters CLICKLINE_LEN_MOD = 0.5 # meters CLICKLINE_SCL = Scale(1, 1, 1) # meters FLOOR_Y = 0.1 # meters GRIDLEN = 20 # meters SCL_HUD = 0.1 # meters PANEL_RADIUS = 1 # meters CLIP_RADIUS = PANEL_RADIUS + 0.25 # meters LOCK_XOFF = 0 # quaternion vector LOCK_YOFF = 0.7 # quaternion vector CLR_HUDTEXT = Color(128, 128, 128) # gray CLR_NUDGE = Color(255, 255, 0) # yellow CLR_SCALE = Color(0, 0, 255) # blue CLR_STRETCH = Color(255, 0, 0) # red CLR_ROTATE = Color(255, 165, 0) # orange CLR_SELECT = Color(255, 255, 0) # yellow CLR_GRID = Color(0, 255, 0) # green CLR_BUTTON = Color(200, 200, 200) # white-ish CLR_BUTTON_DISABLED = Color(128, 128, 128) # gray CLR_BUTTON_TEXT = Color(0, 0, 0) # black OPC_BUTTON = 0.1 # % opacity OPC_BUTTON_HOVER = 0.25 # % opacity OPC_CLINE = 0.1 # % opacity OPC_CLINE_HOVER = 0.9 # % opacity TTL_TEMP = 30 # seconds QUAT_VEC_RGTS = [-1, -0.7, -0.5, 0, 0.5, 0.7, 1]
def show_redpill_scene(enabled): # any scene changes must not persist # show gridlines name = "grid_redpill" path = [] glen = arblib.GRIDLEN y = arblib.FLOOR_Y for z in range(-glen, glen + 1): if (z % 2) == 0: path.append(Position(-glen, y, z)) path.append(Position(glen, y, z)) else: path.append(Position(glen, y, z)) path.append(Position(-glen, y, z)) for x in range(-glen, glen + 1): if (x % 2) == 0: path.append(Position(x, y, glen)) path.append(Position(x, y, -glen)) else: path.append(Position(x, y, -glen)) path.append(Position(x, y, glen)) if enabled: scene.add_object( ThickLine(object_id=name, path=path, color=arblib.CLR_GRID)) else: arblib.delete_obj(scene, name) objs = scene.get_persisted_objs() for object_id in objs: obj = objs[object_id] # show occluded objects if "material-extras" in obj.data and "transparentOccluder" in obj.data[ "material-extras"]: name = "redpill_" + obj.object_id if enabled: object_type = "box" if "object_type" in obj.data: object_type = obj.data.object_type position = Position() if "position" in obj.data: position = obj.data.position rotation = Rotation() if "rotation" in obj.data: rotation = obj.data.rotation scale = Scale() if "scale" in obj.data: scale = obj.data.scale url = None if "url" in obj.data: url = obj.data.url color = Color() if "material" in obj.data and "color" in obj.data.material: color = obj.data.material.color scene.add_object( Object( object_id=name, object_type=object_type, position=position, rotation=rotation, scale=scale, clickable=True, url=url, material=Material(color=color, transparent=True, opacity=0.5), )) print("Wrapping occlusion " + name) else: arblib.delete_obj(scene, name)
def scene_callback(_scene, event, msg): # This is the MQTT message callback function for the scene object_id = action = msg_type = object_type = None if "object_id" in msg: object_id = msg["object_id"] if "action" in msg: action = msg["action"] if "type" in msg: msg_type = msg["type"] if "data" in msg and "object_type" in msg["data"]: object_type = msg["data"]["object_type"] # print(f'{object_type} {action} {msg_type} {object_id}') if object_type == "camera": # camera updates define users present camname = object_id if camname not in USERS: USERS[camname] = arblib.User(scene, camname, panel_callback) # save camera's attitude in the world USERS[camname].position = Position(msg["data"]["position"]["x"], msg["data"]["position"]["y"], msg["data"]["position"]["z"]) USERS[camname].rotation = Rotation(msg["data"]["rotation"]["x"], msg["data"]["rotation"]["y"], msg["data"]["rotation"]["z"], msg["data"]["rotation"]["w"]) rx = msg["data"]["rotation"]["x"] ry = msg["data"]["rotation"]["y"] # floating controller if not USERS[camname].follow_lock: ty = -(ry + USERS[camname].locky) / 0.7 * math.pi / 2 tx = -(rx + USERS[camname].lockx) / 0.7 * math.pi / 2 px = arblib.PANEL_RADIUS * -math.cos(ty) py = arblib.PANEL_RADIUS * math.sin(tx) pz = arblib.PANEL_RADIUS * math.sin(ty) scene.update_object(USERS[camname].follow, position=Position(px, py, pz)) # else: # TODO: panel lock position drop is inaccurate # users[camname].lockx = rx + arblib.LOCK_XOFF # users[camname].locky = -(ry * math.pi) - arblib.LOCK_YOFF # mouse event elif action == "clientEvent": object_id = msg["object_id"] # camera updates define users present camname = msg["data"]["source"] if camname not in USERS: USERS[camname] = arblib.User(scene, camname, panel_callback) # show objects with events if msg_type == EVT_MOUSEENTER: if USERS[camname].redpill: show_redpill_obj(camname, object_id) else: USERS[camname].set_textstatus(object_id) elif msg_type == EVT_MOUSELEAVE: USERS[camname].set_textstatus("") # handle click elif msg_type == EVT_MOUSEDOWN: # clicked on persisted object to modify update_controls(USERS[camname].target_id) USERS[camname].target_id = object_id # always update if USERS[camname].mode == Mode.DELETE: arblib.delete_obj(scene, object_id) elif USERS[camname].mode == Mode.MOVE: do_move_select(camname, object_id) elif USERS[camname].mode == Mode.NUDGE: do_nudge_select(camname, object_id) elif USERS[camname].mode == Mode.SCALE: do_scale_select(camname, object_id) elif USERS[camname].mode == Mode.STRETCH: do_stretch_select(camname, object_id) elif USERS[camname].mode == Mode.ROTATE: do_rotate_select(camname, object_id) elif USERS[camname].mode == Mode.COLOR: arblib.color_obj(scene, object_id, Color(USERS[camname].target_style)) elif USERS[camname].mode == Mode.OCCLUDE: arblib.occlude_obj(scene, object_id, USERS[camname].target_style) elif USERS[camname].mode == Mode.RENAME or USERS[ camname].mode == Mode.PARENT: if len(USERS[camname].typetext) > 0: # edits already made new_id = USERS[camname].typetext USERS[camname].typetext = "" if USERS[camname].mode == Mode.PARENT: arblib.parent_obj(scene, object_id, new_id) else: do_rename(camname, object_id, new_id) else: # no edits yet, load previous name to change USERS[camname].typetext = object_id USERS[camname].set_textright(USERS[camname].typetext)
def make_wall(camname): # Wall theory: capture two poses and use them to place a wall object. # Also assumes first corner easier to capture accurate rotation than last. # Click 1: Capture the position and rotation. # Click 2: Capture the position only. sloc = USERS[camname].wloc_start eloc = USERS[camname].wloc_end srot = USERS[camname].wrot_start erot = USERS[camname].wrot_end print(f"S POS {str(sloc)}") print(f"E POS {str(eloc)}") # center point (blue) locx = statistics.median([sloc.x, eloc.x]) locy = statistics.median([sloc.y, eloc.y]) locz = statistics.median([sloc.z, eloc.z]) pos = Position(locx, locy, locz) scene.add_object(arblib.temp_loc_marker(pos, Color(0, 0, 255))) print(f"wall position {str(pos)}") # rotation print(f"S ROT {str(srot)}") print(f"E ROT {str(erot)}") rotx = arblib.probable_quat(srot.x) roty = arblib.probable_quat(srot.y) rotz = arblib.probable_quat(srot.z) rotw = arblib.probable_quat(srot.w) rot = Rotation(rotx, roty, rotz, rotw) gaze = (rotx, roty, rotz, rotw) scene.add_object(arblib.temp_rot_marker(pos, rot)) print(f"wall rotation {str(rot)}") # which axis to use for wall? use camera gaze # TODO: rotation still off if gaze in arblib.GAZES[0]: height = abs(sloc.y - eloc.y) width = abs(sloc.x - eloc.x) elif gaze in arblib.GAZES[1]: height = abs(sloc.y - eloc.y) width = abs(sloc.z - eloc.z) elif gaze in arblib.GAZES[2]: height = abs(sloc.z - eloc.z) width = abs(sloc.x - eloc.x) else: # TODO: (placeholder) add direction and hypotenuse height = abs(sloc.y - eloc.y) width = abs(sloc.x - eloc.x) print(f"Non-axis parallel rotation: {str(rot)}") # scale scax = width scay = height scaz = arblib.WALL_WIDTH sca = Scale(scax, scay, scaz) print(f"wall scale {str(sca)}") # make wall randstr = str(random.randrange(0, 1000000)) new_wall = Box( persist=True, clickable=True, object_id=f"wall_{randstr}", position=pos, rotation=rot, scale=sca, material=Material(color=Color(200, 200, 200), transparent=True, opacity=0.5), ) scene.add_object(new_wall) USERS[camname].target_id = new_wall.object_id print(f"Created {new_wall.object_id} r{str(rot)} s{str(sca)}")