<!-- RUBY-BACKUP · part 338 -->
on.z / _m) * _m;
            if(typeof codeStatus==='function') codeStatus('snapped to grid');
          } else if(typeof codeStatus==='function') codeStatus('select a primitive first');
          break;
        case 'smooth':
          if(window._selectedPrimitive){
            window._selectedPrimitive.traverse(function(o){
              if(o.isMesh && o.material){ o.material = o.material.clone(); o.material.roughness = 0.9; o.material.metalness = 0.1; }
            });
            if(typeof codeStatus==='function') codeStatus('smoothed');
          } else if(typeof codeStatus==='function') codeStatus('select a primitive first');
          break;
        case 'sharpen':
          if(window._selectedPrimitive){
            window._selectedPrimitive.traverse(function(o){
              if(o.isMesh && o.material){ o.material = o.material.clone(); o.material.roughness = 0.1; o.material.metalness = 0.9; }
            });
            if(typeof codeStatus==='function') codeStatus('sharpened');
          } else if(typeof codeStatus==='function') codeStatus('select a primitive first');
          break;
        case 'hollow':
          if(window._selectedPrimitive){
            window._selectedPrimitive.traverse(function(o){
              if(o.isMesh && o.material){ o.material = o.material.clone(); o.material.transparent = true; o.material.opacity = 0.4; o.material.side = THREE.DoubleSide; }
            });
            if(typeof codeStatus==='function') codeStatus('hollowed');
          } else if(typeof codeStatus==='function') codeStatus('select a primitive first');
          break;
        case 'rotate':
          if(window._selectedPrimitive){
            window._selectedPrimitive.rotation.y += Math.PI / 8;
            if(typeof codeStatus==='function') codeStatus('rotated');
            if(typeof pushCommand === 'function') pushCommand({
              name: 'rotate', execute: function(){}, undo: function(){ window._selectedPrimitive.rotation.y -= Math.PI / 8; }
            });
          } else if(typeof codeStatus==='function') codeStatus('select a primitive first');
          break;
        case 'scale':
          if(window._selectedPrimitive){
            var _s = window._selectedPrimitive.scale.x;
            var _ns = _s >= 2 ? 0.5 : _s + 0.5;
            window._selectedPrimitive.scale.set(_ns, _ns, _ns);
            if(typeof codeStatus==='function') codeStatus('scaled to ' + _ns.toFixed(1));
          } else if(typeof codeStatus==='function') codeStatus('select a primitive first');
          break;
        case 'duplicate':
          if(window._selectedPrimitive){
            try{
              var _dup = window._selectedPrimitive.clone();
              _dup.position.x += 3; _dup.position.z += 3;
              if(typeof scene !== 'undefined') scene.add(_dup);
              _dup.userData.isPrimitive = true;
              var _sr2 = (typeof scene !== 'undefined') ? scene : null;
              if(typeof pushCommand === 'function') pushCommand({
                name: 'duplicate', execute: function(){ if(_sr2) _sr2.add(_dup); }, undo: function(){ if(_sr2) _sr2.remove(_dup); }
              });
              if(typeof codeStatus==='function') codeStatus('duplicated');
            }catch(e){}
          } else if(typeof codeStatus==='function') codeStatus('select a primitive first');
          break;
        case 'color':
          if(window._selectedPrimitive){
            /* Cycle through gold, marble, bronze, obsidian */
            var _colors = [0xd4a017, 0xf5f5f5, 0x8b5a2b, 0x1a1a1a, 0xff4400];
            window._colorIdx = ((window._colorIdx || 0) + 1) % _colors.length;
            window._selectedPrimitive.traverse(function(o){
              if(o.isMesh && o.material){ o.material = o.material.clone(); o.material.color.setHex(_colors[window._colorIdx]); }
            });
            if(typeof codeStatus==='function') codeStatus('color changed');
          } else if(typeof codeStatus==='function') codeStatus('select a primitive first');
          break;
        case 'grab': if(typeof toggleGrabMode==='function') toggleGrabMode(); break;
        default: if(typeof codeStatus==='function') codeStatus(fn);
      }
    }catch(e){}
  }
  function close(){
    OPEN = false;
    var c = document.getElementById('creator');
    if(c) c.remove();
    var s = document.getElementById('creator-css');
    if(s) s.remove();
    el = null;
  }
  /* Tap a primitive to select it */
  if(!window._creatorTapWired){
    window._creatorTapWired = true;
    document.addEventListener('click', function(e){
      if(!window._creatorSelectMode) return;
      try{
        if(typeof raycaster === 'undefined' || typeof camera === 'undefined') return;
        var mv = new THREE.Vector2((e.clientX/window.innerWidth)*2-1, -(e.clientY/window.innerHeight)*2+1);
        raycaster.setFromCamera(mv, camera);
        var hits = raycaster.intersectObjects(scene.children, true);
        for(var hi = 0; hi < hits.