Skip to content
This repository has been archived by the owner on Jun 10, 2022. It is now read-only.

Add inverted group mask #608

Merged
merged 3 commits into from
Jun 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions packages/functional-tests/src/tests/user-mask-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ export default class UserMaskTest extends Test {
const red = this.assets.createMaterial('redMat', {
color: { r: .854, g: 0.132, b: 0.132 }
});
const not = this.assets.createMaterial('notMat', {
color: MRE.Color3.Gray()
});
const box = this.assets.createBoxMesh('box', 0.5, 0.5, 0.5);
const sphere = this.assets.createSphereMesh('sphere', 0.3);

// create team labels
const textDef = {
Expand Down Expand Up @@ -63,7 +68,7 @@ export default class UserMaskTest extends Test {
parentId: root.id,
appearance: {
enabled: new MRE.GroupMask(this.app.context, ['red', 'default']),
meshId: this.assets.createBoxMesh('box', 0.5, 0.5, 0.5).id,
meshId: box.id,
materialId: red.id
},
collider: { geometry: { shape: MRE.ColliderType.Auto } },
Expand All @@ -73,13 +78,31 @@ export default class UserMaskTest extends Test {
}
});

MRE.Actor.Create(this.app.context, {
actor: {
name: 'notRedIcon',
parentId: root.id,
appearance: {
enabled: new MRE.InvertedGroupMask(this.app.context, ['red', 'default']),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InvertedGroupMask [](start = 22, length = 17)

Does this mean not in the default group as well? Or is every user alway in the default group?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not in the default group, correct.

Copy link
Contributor Author

@stevenvergenz stevenvergenz Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify: every user who has no other groups assigned is in the "default" group. So if "default" is in an inverted group mask, then those users will not see the given object.


In reply to: 437673473 [](ancestors = 437673473)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So then it is possible to use the default group for filtering now as well since you can set and object for the "not default group"?


In reply to: 437683618 [](ancestors = 437683618,437673473)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could always use the default group for filtering. Remember, the default group is not all users, it's only users with no other groups assigned. Just now you can apply inverted semantics. It's just like any other group.


In reply to: 437688309 [](ancestors = 437688309,437683618,437673473)

meshId: box.id,
materialId: not.id
},
transform: {
local: {
position: { y: 0.35 },
scale: { x: 0.5, y: 0.5, z: 0.5 }
}
}
}
});

const blueIcon = MRE.Actor.Create(this.app.context, {
actor: {
name: 'blueIcon',
parentId: root.id,
appearance: {
enabled: new MRE.GroupMask(this.app.context, ['blue', 'default']),
meshId: this.assets.createSphereMesh('sphere', 0.3).id,
meshId: sphere.id,
materialId: blue.id
},
collider: { geometry: { shape: MRE.ColliderType.Auto } },
Expand All @@ -89,6 +112,24 @@ export default class UserMaskTest extends Test {
}
});

MRE.Actor.Create(this.app.context, {
actor: {
name: 'notBlueIcon',
parentId: root.id,
appearance: {
enabled: new MRE.InvertedGroupMask(this.app.context, ['blue', 'default']),
meshId: sphere.id,
materialId: not.id
},
transform: {
local: {
position: { y: 0.35 },
scale: { x: 0.5, y: 0.5, z: 0.5 }
}
}
}
});

// blink the icons for unaffiliated users
this.interval = setInterval(() => {
if (redIcon.appearance.enabledFor.has('default')) {
Expand Down
27 changes: 20 additions & 7 deletions packages/sdk/src/user/groupMask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { Context } from '..';
import { Context, InvertedGroupMask } from '..';

/**
* A set of user group IDs. User groups are used to selectively enable several different
Expand All @@ -26,7 +26,7 @@ export class GroupMask extends Set<string> implements Iterable<string> {
* @param context An active MRE app context
* @param initialContents A list of group names to be added to this GroupMask
*/
constructor(private context: Context, initialContents: Iterable<string> = null) {
constructor(protected context: Context, initialContents: Iterable<string> = null) {
super();
if (initialContents) {
for (const group of initialContents) {
Expand All @@ -48,9 +48,16 @@ export class GroupMask extends Set<string> implements Iterable<string> {

/** @hidden */
public static FromPacked(context: Context, value: number): GroupMask {
const group = new GroupMask(context);
group.setPacked(value);
return group;
const largerThanBiggestGroup = Object.keys(context.internal.userGroupMapping).length;
if (value >= (1 << largerThanBiggestGroup)) {
const igroup = new InvertedGroupMask(context);
igroup.setPacked(value);
return igroup;
} else {
const group = new GroupMask(context);
group.setPacked(value);
return group;
}
}

/** @hidden */
Expand All @@ -70,7 +77,8 @@ export class GroupMask extends Set<string> implements Iterable<string> {
return this.packed();
}

private getOrAddMapping(name: string): number {
/** @hidden */
protected getOrAddMapping(name: string): number {
const mapping = this.context.internal.userGroupMapping;
if (!mapping[name]) {
const lastIndex = Object.keys(mapping).length;
Expand All @@ -89,7 +97,7 @@ export class GroupMask extends Set<string> implements Iterable<string> {
* Observable considerations
*/

private changedCallback: (gm: GroupMask) => void;
protected changedCallback: (gm: GroupMask) => void;

/** @hidden */
public onChanged(callback: (gm: GroupMask) => void) {
Expand Down Expand Up @@ -189,4 +197,9 @@ export class GroupMask extends Set<string> implements Iterable<string> {
public has(item: string) {
return super.has(item);
}

/** Convert this from a mask containing only these groups to a mask containing everything but these groups. */
public invert() {
return new InvertedGroupMask(this.context, this);
}
}
1 change: 1 addition & 0 deletions packages/sdk/src/user/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
*/

export * from './groupMask';
export * from './invertedGroupMask';
export * from './user';
39 changes: 39 additions & 0 deletions packages/sdk/src/user/invertedGroupMask.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*!
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { GroupMask } from './groupMask';

/**
* A collection of user group IDs. Unlike in [[GroupMask]], users in these groups are **excluded** from this mask
* instead of included.
*/
export class InvertedGroupMask extends GroupMask {
/** @hidden */
public packed() {
return ~super.packed();
}

/** @hidden */
public setPacked(value: number) {
super.clear();
const mapping = this.context.internal.userGroupMapping;

if (!this.allowDefault) {
value = value & ~this.getOrAddMapping('default');
}
for (const name of Object.keys(mapping)) {
if ((value & this.getOrAddMapping(name)) === 0) {
super.add(name);
}
}
if (this.changedCallback) {
this.changedCallback(this);
}
}

/** Convert this from a mask containing everything but these groups to a mask containing only these groups. */
public invert() {
return new GroupMask(this.context, this);
}
}