def boxline(object_id, axis, direction, delim, suffix, start, end, line_width, move, color, parent): line_width = line_width / SCL_CLICK end = Position(x=(end.x - start.x) / SCL_CLICK, y=(end.y - start.y) / SCL_CLICK, z=(end.z - start.z) / SCL_CLICK) if start.y == end.y and start.z == end.z: scale = Scale(x=abs(start.x - end.x), y=line_width, z=line_width) elif start.x == end.x and start.z == end.z: scale = Scale(x=line_width, y=abs(start.y - end.y), z=line_width) elif start.x == end.x and start.y == end.y: scale = Scale(x=line_width, y=line_width, z=abs(start.z - end.z)) name = f"{object_id}{delim}{axis}{direction}_{suffix}" position = Position(statistics.median([start.x, end.x]), statistics.median([start.y, end.y]), statistics.median([start.z, end.z])) if name not in CONTROLS[object_id]: CONTROLS[object_id][name] = Box( object_id=name, # TODO: restore ttl=arblib.TTL_TEMP, parent=parent, scale=scale, position=position, material=Material(color=color, transparent=True, opacity=0.4, shader="flat"), ) scene.add_object(CONTROLS[object_id][name]) elif move: scene.update_object(CONTROLS[object_id][name], position=position, scale=scale)
def do_scale_select(camname, objid, scale=None): color = arblib.CLR_SCALE delim = f"_{Mode.SCALE.value}_" callback = scaleline_callback obj = scene.get_persisted_obj(objid) position = Position() if "position" in obj.data: position = obj.data.position if not scale: scale = Scale() if "scale" in obj.data: scale = obj.data.scale xl, yl, zl = get_clicklines_len(obj) # scale entire object + or - on all axis root = make_clickroot(objid, position, delim, move=True) make_clickline("x", xl, objid, position, delim, color, callback, move=True, parent=root) # TODO: restore make_followspot(objid, position, delim, color) sca = Scale(round(scale.x, 3), round(scale.y, 3), round(scale.z, 3)) USERS[camname].set_textright( f"{USERS[camname].target_style} s({sca.x},{sca.y},{sca.z})")
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 dir_clickers(object_id, axis, direction, delim, position, color, cones, callback, move, parent): position = Position(x=position.x / SCL_CLICK, y=position.y / SCL_CLICK, z=position.z / SCL_CLICK) loc = Position(x=position.x / SCL_CLICK, y=position.y / SCL_CLICK, z=position.z / SCL_CLICK) npos = 0.1 / SCL_CLICK if direction == "p": npos = -0.1 / SCL_CLICK if axis == "x": loc = Position(x=position.x + npos, y=position.y, z=position.z) elif axis == "y": loc = Position(x=position.x, y=position.y + npos, z=position.z) elif axis == "z": loc = Position(x=position.x, y=position.y, z=position.z + npos) name_pos = f"{object_id}{delim}{axis}p_{direction}" name_neg = f"{object_id}{delim}{axis}n_{direction}" if name_pos not in CONTROLS[object_id]: CONTROLS[object_id][name_pos] = Cone( # click object positive object_id=name_pos, clickable=True, position=position, rotation=cones[axis + direction][0], scale=Scale(0.05 / SCL_CLICK, 0.09 / SCL_CLICK, 0.05 / SCL_CLICK), material=Material(color=color, transparent=True, opacity=arblib.OPC_CLINE), # TODO: restore ttl=arblib.TTL_TEMP, parent=parent, evt_handler=callback) scene.add_object(CONTROLS[object_id][name_pos]) elif move: scene.update_object(CONTROLS[object_id][name_pos], position=position) if name_neg not in CONTROLS[object_id]: CONTROLS[object_id][name_neg] = Cone( # click object negative object_id=name_neg, clickable=True, position=loc, rotation=cones[axis + direction][1], scale=Scale(0.05 / SCL_CLICK, 0.09 / SCL_CLICK, 0.05 / SCL_CLICK), material=Material(color=color, transparent=True, opacity=arblib.OPC_CLINE), # TODO: restore ttl=arblib.TTL_TEMP, parent=parent, evt_handler=callback) scene.add_object(CONTROLS[object_id][name_neg]) elif move: scene.update_object(CONTROLS[object_id][name_neg], position=loc)
def temp_loc_marker(position, color): return Sphere(ttl=120, material=Material(color=color, transparent=True, opacity=0.5), position=position, scale=Scale(0.02, 0.02, 0.02), clickable=True)
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 get_clipboard(self): obj_actual = self.clipboard obj_actual.data.scale = Scale( self.clipboard.data.scale.x*SCL_HUD, self.clipboard.data.scale.y*SCL_HUD, self.clipboard.data.scale.z*SCL_HUD) return obj_actual
def get_clicklines_len(obj): 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 if object_type == "gltf-model": # TODO: if we can get the gltf size (not scale), we can make accurate clicklines scale = Scale(1, 1, 1) line_extension = arblib.CLICKLINE_LEN_MOD else: line_extension = arblib.CLICKLINE_LEN_OBJ xl = scale.x + line_extension yl = scale.y + line_extension zl = scale.z + line_extension print(xl, yl, zl) return xl, yl, zl
def scaleline_callback(_scene, event, msg): obj, direction, move = handle_clickline_event(event, Mode.SCALE) if not obj or not direction or "scale" not in obj.data: return scaled = sca = obj.data.scale inc = meters_increment(USERS[event.data.source].target_style) if direction == "xp": scaled = Scale(x=incr_pos(sca.x, inc), y=incr_pos(sca.y, inc), z=incr_pos(sca.z, inc)) elif direction == "xn": scaled = Scale(x=incr_neg(sca.x, inc), y=incr_neg(sca.y, inc), z=incr_neg(sca.z, inc)) if scaled.x <= 0 or scaled.y <= 0 or scaled.z <= 0: return arblib.scale_obj(scene, obj.object_id, scaled) print(f"{str(obj.data.scale)} to {str(scaled)}") do_scale_select(event.data.source, obj.object_id, scale=scaled)
def make_hudtext(self, label, position, text): text = Text( object_id=f"{label}_{self.camname}", parent=self.hud.object_id, text=text, position=Position(position.x / SCL_HUD, position.y / SCL_HUD, position.z / SCL_HUD), color=CLR_HUDTEXT, scale=Scale(0.1 / SCL_HUD, 0.1 / SCL_HUD, 0.1 / SCL_HUD), ) self.scene.add_object(text) return text
def model_callback(_scene, event, msg): camname, objid, drop = handle_panel_event(event, dropdown=True) if not camname or not drop: return model = drop idx = MODELS.index(model) url = MANIFEST[idx]['url_gltf'] sca = MANIFEST[idx]['scale'] USERS[camname].set_clipboard(callback=clipboard_callback, object_type=GLTF.object_type, scale=Scale(sca, sca, sca), url=url) USERS[camname].set_textright(model) USERS[camname].target_style = model
def make_followspot(object_id, position, delim, color, parent, move=False): name = f"{object_id}{delim}spot" if name not in CONTROLS[object_id]: CONTROLS[object_id][name] = Circle( # follow spot on ground object_id=name, scale=Scale(0.1, 0.1, 0.1), # TODO: restore ttl=arblib.TTL_TEMP, position=Position(position.x, arblib.FLOOR_Y, position.z), rotation=Rotation(-0.7, 0, 0, 0.7), parent=parent, material=Material(color=color, transparent=True, opacity=0.4, shader="flat"), ) scene.add_object(CONTROLS[object_id][name]) elif move: scene.update_object(CONTROLS[object_id][name], position=position)
def show_redpill_obj(camname, object_id): # any scene changes must not persist obj = scene.get_persisted_obj(object_id) # enable mouse enter/leave pos/rot/scale 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 USERS[camname].set_textstatus(" ".join([ f"{object_id}", f"p({position.x},{position.y},{position.z})", f"r({rotation.x},{rotation.y},{rotation.z},{rotation.w})", f"s({scale.x},{scale.y},{scale.z})" ]))
def make_clickroot(objid, position, delim, rotation=None, move=False): if objid not in CONTROLS.keys(): CONTROLS[objid] = {} name = f"{objid}{delim}clickroot" if rotation: name += "_rotated" else: rotation = Rotation(0, 0, 0, 1) if name not in CONTROLS[objid]: CONTROLS[objid][name] = Box( object_id=name, material=Material(transparent=True, opacity=0), position=position, scale=Scale(SCL_CLICK, SCL_CLICK, SCL_CLICK), rotation=rotation, ) scene.add_object(CONTROLS[objid][name]) elif move: scene.update_object(CONTROLS[objid][name], position=position, rotation=rotation) return name
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, )
def stretchline_callback(_scene, event, msg): obj, direction, move = handle_clickline_event(event, Mode.STRETCH) if not obj or not direction or not move or "scale" not in obj.data or "position" not in obj.data: return scaled = sca = obj.data.scale moved = loc = obj.data.position inc = meters_increment(USERS[event.data.source].target_style) if direction == "xp": scaled = Scale(x=incr_pos(sca.x, inc), y=sca.y, z=sca.z) moved = Position(x=recenter(scaled.x, sca.x, loc.x, move), y=loc.y, z=loc.z) elif direction == "xn": scaled = Scale(x=incr_neg(sca.x, inc), y=sca.y, z=sca.z) moved = Position(x=recenter(scaled.x, sca.x, loc.x, move), y=loc.y, z=loc.z) elif direction == "yp": scaled = Scale(x=sca.x, y=incr_pos(sca.y, inc), z=sca.z) moved = Position(x=loc.x, y=recenter(scaled.y, sca.y, loc.y, move), z=loc.z) elif direction == "yn": scaled = Scale(x=sca.x, y=incr_neg(sca.y, inc), z=sca.z) moved = Position(x=loc.x, y=recenter(scaled.y, sca.y, loc.y, move), z=loc.z) elif direction == "zp": scaled = Scale(x=sca.x, y=sca.y, z=incr_pos(sca.z, inc)) moved = Position(x=loc.x, y=loc.y, z=recenter(scaled.z, sca.z, loc.z, move)) elif direction == "zn": scaled = Scale(x=sca.x, y=sca.y, z=incr_neg(sca.z, inc)) moved = Position(x=loc.x, y=loc.y, z=recenter(scaled.z, sca.z, loc.z, move)) if scaled.x <= 0 or scaled.y <= 0 or scaled.z <= 0: return arblib.stretch_obj(scene, obj.object_id, scale=scaled, position=moved) print(f"{str(obj.data.scale)} to {str(scaled)}") do_stretch_select(event.data.source, obj.object_id, scale=scaled)
def __init__(self, scene: Scene, camname, mode, x=0, y=0, label="", parent=None, drop=None, color=CLR_BUTTON, enable=True, callback=None, btype=ButtonType.ACTION): self.scene = scene if label == "": label = mode.value if parent is None: parent = camname scale = Scale(0.1, 0.1, 0.01) else: scale = Scale(1, 1, 1) self.type = btype self.enabled = enable if enable: self.colorbut = color else: self.colorbut = CLR_BUTTON_DISABLED self.colortxt = CLR_BUTTON_TEXT if len(label) > 8: # easier to read self.label = f"{label[:6]}..." else: self.label = label self.mode = mode self.dropdown = drop self.active = False if drop is None: obj_name = f"{camname}_button_{mode.value}" else: obj_name = f"{camname}_button_{mode.value}_{drop}" shape = Box.object_type if btype == ButtonType.TOGGLE: shape = Cylinder.object_type scale = Scale(scale.x / 2, scale.y, scale.z / 2) self.button = Object( # box is main button object_id=obj_name, object_type=shape, parent=parent, material=Material( color=self.colorbut, transparent=True, opacity=OPC_BUTTON, shader="flat"), position=Position(x * 1.1, PANEL_RADIUS, y * -1.1), scale=scale, clickable=True, evt_handler=callback, ) scene.add_object(self.button) scale = Scale(1, 1, 1) if btype == ButtonType.TOGGLE: scale = Scale(scale.x * 2, scale.y * 2, scale.z) self.text = Text( # text child of button object_id=f"{self.button.object_id}_text", parent=self.button.object_id, text=self.label, # position inside to prevent ray events position=Position(0, -0.1, 0), rotation=Rotation(-0.7, 0, 0, 0.7), scale=scale, color=self.colortxt, ) scene.add_object(self.text)
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 panel_callback(_scene, event, msg): camname, objid, drop = handle_panel_event(event) if not camname or not objid: return # ignore disabled if not USERS[camname].panel[objid].enabled: return update_controls(USERS[camname].target_id) mode = USERS[camname].panel[objid].mode btype = USERS[camname].panel[objid].type if btype == ButtonType.TOGGLE: USERS[camname].panel[objid].set_active( not USERS[camname].panel[objid].active) else: if mode == USERS[camname].mode: # action cancel # button click is same, then goes off and NONE USERS[camname].panel[objid].set_active(False) USERS[camname].mode = Mode.NONE mode = Mode.NONE else: # if button goes on, last button must go off prev_objid = f"{camname}_button_{USERS[camname].mode.value}" if prev_objid in USERS[camname].panel: USERS[camname].panel[prev_objid].set_active(False) USERS[camname].panel[objid].set_active(True) USERS[camname].mode = mode USERS[camname].set_textleft(USERS[camname].mode) USERS[camname].set_textright("") USERS[camname].del_clipboard() # clear last dropdown for but in USERS[camname].dbuttons: USERS[camname].dbuttons[but].delete() USERS[camname].dbuttons.clear() active = USERS[camname].panel[objid].active # toggle buttons if mode == Mode.LOCK: USERS[camname].follow_lock = active # TODO: after lock ensure original ray keeps lock button in reticle elif mode == Mode.REDPILL: # TODO: migrate to shared-scene setting USERS[camname].redpill = active show_redpill_scene(active) elif mode == Mode.LAMP: USERS[camname].set_lamp(active) # active buttons if mode == Mode.CREATE: update_dropdown(camname, objid, mode, arblib.SHAPES, 2, shape_callback) USERS[camname].set_clipboard(callback=clipboard_callback, object_type=USERS[camname].target_style) elif mode == Mode.MODEL: update_dropdown(camname, objid, mode, MODELS, 2, model_callback) idx = MODELS.index(USERS[camname].target_style) url = MANIFEST[idx]['url_gltf'] sca = MANIFEST[idx]['scale'] USERS[camname].set_clipboard(callback=clipboard_callback, object_type=GLTF.object_type, scale=Scale(sca, sca, sca), url=url) elif mode == Mode.COLOR: update_dropdown(camname, objid, mode, arblib.COLORS, -2, color_callback) elif mode == Mode.OCCLUDE: update_dropdown(camname, objid, mode, arblib.BOOLS, -2, gen_callback) elif mode == Mode.RENAME: USERS[camname].typetext = "" update_dropdown(camname, objid, mode, arblib.KEYS, -2, rename_callback) USERS[camname].set_textright(USERS[camname].typetext) elif mode == Mode.PARENT: USERS[camname].typetext = "" USERS[camname].set_textright(USERS[camname].typetext) elif mode == Mode.WALL: USERS[camname].set_clipboard(callback=wall_callback) USERS[camname].set_textright("Start: tap flush corner.") elif mode == Mode.NUDGE: update_dropdown(camname, objid, mode, arblib.METERS, 2, gen_callback) if USERS[camname].target_id: do_nudge_select(camname, USERS[camname].target_id) elif mode == Mode.SCALE: update_dropdown(camname, objid, mode, arblib.METERS, 2, gen_callback) if USERS[camname].target_id: do_scale_select(camname, USERS[camname].target_id) elif mode == Mode.STRETCH: update_dropdown(camname, objid, mode, arblib.METERS, 2, gen_callback) if USERS[camname].target_id: do_stretch_select(camname, USERS[camname].target_id) elif mode == Mode.ROTATE: update_dropdown(camname, objid, mode, arblib.DEGREES, 2, gen_callback) if USERS[camname].target_id: do_rotate_select(camname, USERS[camname].target_id)
def __init__(self, scene: Scene, camname, panel_callback): self.scene = scene self.camname = camname self.mode = Mode.NONE self.clipboard = self.cliptarget = None self.target_id = self.target_control_id = None self.position = self.rotation = None self.position_last = self.rotation_last = None self.gesturing = False self.target_style = self.typetext = "" self.locky = LOCK_YOFF self.lockx = LOCK_XOFF self.wloc_start = self.wloc_end = None self.wrot_start = self.wrot_end = None self.lamp = None init_origin(self.scene) # set HUD to each user self.hud = Box( object_id=f"hud_{camname}", parent=camname, material=Material(transparent=True, opacity=0), position=Position(0, 0, 0), scale=Scale(SCL_HUD, SCL_HUD, SCL_HUD), rotation=Rotation(0, 0, 0, 1), ) self.scene.add_object(self.hud) self.hudtext_left = self.make_hudtext( "hudTextLeft", Position(-0.15, 0.15, -0.5), str(self.mode)) self.hudtext_right = self.make_hudtext( "hudTextRight", Position(0.1, 0.15, -0.5), "") self.hudtext_status = self.make_hudtext( "hudTextStatus", Position(0.02, -0.15, -0.5), "") # workaround x=0 bad? # AR Control Panel self.follow_lock = False self.follow = Box( object_id=f"follow_{camname}", parent=camname, material=Material(transparent=True, opacity=0), position=Position(0, 0, -PANEL_RADIUS * 0.1), scale=Scale(0.1, 0.01, 0.1), rotation=Rotation(0.7, 0, 0, 0.7), ) self.scene.add_object(self.follow) self.redpill = False self.slider = False self.panel = {} # button dictionary self.dbuttons = {} buttons = [ # top row [Mode.ROTATE, -2, 1, True, ButtonType.ACTION], [Mode.NUDGE, -1, 1, True, ButtonType.ACTION], [Mode.SCALE, 0, 1, True, ButtonType.ACTION], [Mode.STRETCH, 1, 1, True, ButtonType.ACTION], [Mode.MODEL, 2, 1, True, ButtonType.ACTION], [Mode.CREATE, 3, 1, True, ButtonType.ACTION], # center row [Mode.REDPILL, -2, 0, True, ButtonType.TOGGLE], [Mode.MOVE, -1, 0, True, ButtonType.ACTION], [Mode.LOCK, 0, 0, True, ButtonType.TOGGLE], [Mode.DELETE, 1, 0, True, ButtonType.ACTION], [Mode.PARENT, 2, 0, True, ButtonType.ACTION], # bottom row [Mode.WALL, -2, -1, True, ButtonType.ACTION], [Mode.OCCLUDE, -1, -1, True, ButtonType.ACTION], [Mode.RENAME, 0, -1, True, ButtonType.ACTION], [Mode.COLOR, 1, -1, True, ButtonType.ACTION], [Mode.LAMP, 2, -1, True, ButtonType.TOGGLE], [Mode.SLIDER, 3, -1, False, ButtonType.TOGGLE], # TODO: adjust scale ] for but in buttons: pbutton = Button( scene, camname, but[0], but[1], but[2], enable=but[3], btype=but[4], parent=self.follow.object_id, callback=panel_callback) self.panel[pbutton.button.object_id] = pbutton
# AR Builder Library # Utility classes and methods for AR Builder. # pylint: disable=missing-docstring import enum import scipy 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
def do_stretch_select(camname, objid, scale=None): color = arblib.CLR_STRETCH delim = f"_{Mode.STRETCH.value}_" callback = stretchline_callback obj = scene.get_persisted_obj(objid) position = Position() if "position" in obj.data: position = obj.data.position if not scale: object_type = "box" if "object_type" in obj.data: object_type = obj.data.object_type rotation = Rotation() if "rotation" in obj.data: rotation = obj.data.rotation scale = Scale() if "scale" in obj.data: scale = obj.data.scale # TODO: scale too unpredictable, modify if object_type == GLTF.object_type: return # TODO: scale too unpredictable, modify if rotation.quaternion.__dict__ != Rotation(x=0, y=0, z=0, w=1).quaternion.__dict__: return xl, yl, zl = get_clicklines_len(obj) # scale and reposition on one of 6 sides root = make_clickroot(objid, position, delim, move=True) make_clickline("x", xl, objid, position, delim, color, callback, move=True, parent=root) make_clickline("x", -xl, objid, position, delim, color, callback, move=True, parent=root) make_clickline("y", yl, objid, position, delim, color, callback, move=True, parent=root) make_clickline("y", -yl, objid, position, delim, color, callback, move=True, parent=root) make_clickline("z", zl, objid, position, delim, color, callback, move=True, parent=root) make_clickline("z", -zl, objid, position, delim, color, callback, move=True, parent=root) # TODO: restore make_followspot(objid, position, delim, color) sca = Scale(round(scale.x, 3), round(scale.y, 3), round(scale.z, 3)) USERS[camname].set_textright( f"{USERS[camname].target_style} s({sca.x},{sca.y},{sca.z})")
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)}")