pick list dialog

This commit is contained in:
Val Erastov 2022-03-24 01:19:40 -07:00
parent 4e9961669b
commit f6c75d9c23
21 changed files with 401 additions and 95 deletions

View file

@ -11,7 +11,7 @@ interface Stream<T> extends Observable<T> {
pairwise(first?: T): Stream<[T, T]>; pairwise(first?: T): Stream<[T, T]>;
scan<T>(seed: T, scanFn: (accum: T, current: T) => T): Stream<T>; scan<R>(seed: R, scanFn: (accum: R, current: T) => R): Stream<R>;
remember(initialValue: T, usingStream?: any): StateStream<T> remember(initialValue: T, usingStream?: any): StateStream<T>

View file

@ -10,6 +10,7 @@ export class ScanStream extends StreamBase {
} }
attach(observer) { attach(observer) {
observer(this.value);
return this.stream.attach(v => { return this.stream.attach(v => {
this.value = this.scanFunc(this.value, v); this.value = this.scanFunc(this.value, v);
observer(this.value); observer(this.value);

View file

@ -1,8 +1,5 @@
.objectItem {
@itemRadius: 5px;
@alt-color: #9c9c9c;
.button { .button() {
cursor: pointer; cursor: pointer;
&:hover { &:hover {
background-color: #0074D9; background-color: #0074D9;
@ -10,8 +7,11 @@
&:active { &:active {
background-color: #000d7f; background-color: #000d7f;
} }
} }
.objectItem {
@itemRadius: 5px;
@alt-color: #9c9c9c;
background-color: rgba(0,0,0, 0.6); background-color: rgba(0,0,0, 0.6);
border: 1px solid #000; border: 1px solid #000;
@ -48,25 +48,27 @@
&.selected { &.selected {
background: linear-gradient(#59acff, #0074D9); background: linear-gradient(#59acff, #0074D9);
} }
&.highlighted {
background-color: #0074D9;
} }
.onOffButton {
display: flex;
align-items: center;
padding: 0 4px;
}
.onOffButton.on {
cursor: pointer;
background-color: #005e82;
.button;
}
.onOffButton.off {
cursor: pointer;
background-color: #5f5f5f;
.button;
} }
} }
.onOffButton {
display: flex;
align-items: center;
padding: 0 4px;
}
.onOffButton.on {
cursor: pointer;
background-color: #005e82;
.button;
}
.onOffButton.off {
cursor: pointer;
background-color: #5f5f5f;
.button;
}

View file

@ -23,6 +23,7 @@ interface GenericExplorerNodeProps {
controls: any; controls: any;
label: any; label: any;
selected: boolean; selected: boolean;
highlighted: boolean;
defaultExpanded?: boolean; defaultExpanded?: boolean;
expandable: boolean; expandable: boolean;
select: any; select: any;
@ -46,7 +47,7 @@ export function GenericExplorerNode(props: GenericExplorerNodeProps) {
{props.controls} {props.controls}
<span onClick={props.select} <span onClick={props.select}
className={cx(ls.objectLabel, props.selected && ls.selected)}> className={cx(ls.objectLabel, props.selected && ls.selected, props.highlighted && ls.highlighted)}>
{props.label} {props.label}
</span> </span>

View file

@ -26,6 +26,23 @@ pre {
background-color: @color-highlight; background-color: @color-highlight;
} }
.highlighted {
background-color: @on-color-highlight;
}
.selected {
background-color: @color-btn-selected;
}
.pointer {
cursor: pointer;
}
.centered-row {
display: inline-flex;
align-items: center;
}
path { path {
stroke: currentColor; stroke: currentColor;
} }

View file

@ -40,9 +40,9 @@
@color-neutral: #66727d; @color-neutral: #66727d;
@color-highlight: #003f5d; @color-highlight: #003f5d;
@color-btn-selected: hsl(@hue-prim, 55%, 46%); @color-btn-selected: #285f7a;
@on-color-highlight: lightskyblue; @on-color-highlight: #5A93BBFF;
@on-color-highlight-variant-yellow: bisque; @on-color-highlight-variant-yellow: bisque;
@on-color-highlight-variant-pink: hotpink; @on-color-highlight-variant-pink: hotpink;
@on-color-highlight-variant-red: tomato; @on-color-highlight-variant-red: tomato;

View file

@ -1,4 +1,4 @@
import React, {useContext} from "react"; import React, {useContext, useEffect} from "react";
import {useStreamWithPatcher, useStreamWithUpdater} from "ui/effects"; import {useStreamWithPatcher, useStreamWithUpdater} from "ui/effects";
import Stack from "ui/components/Stack"; import Stack from "ui/components/Stack";
import Field from "ui/components/controls/Field"; import Field from "ui/components/controls/Field";
@ -8,6 +8,7 @@ import CheckboxControl from "ui/components/controls/CheckboxControl";
import {AppContext} from "cad/dom/components/AppContext"; import {AppContext} from "cad/dom/components/AppContext";
import {ModelAttributes} from "cad/attributes/attributesService"; import {ModelAttributes} from "cad/attributes/attributesService";
import {GenericWizard} from "ui/components/GenericWizard"; import {GenericWizard} from "ui/components/GenericWizard";
import {View} from "cad/scene/views/view";
export function DisplayOptionsDialogManager() { export function DisplayOptionsDialogManager() {
@ -46,12 +47,20 @@ export function DisplayOptionsView(props: DisplayOptionsViewProps) {
const ctx = useContext(AppContext); const ctx = useContext(AppContext);
const streamsAndPatchers: [ModelAttributes, any][] = []; const streamsAndPatchers: [ModelAttributes, any][] = [];
useEffect(()=>{
return () => {
View.SUPPRESS_HIGHLIGHTS = false;
ctx.viewer.requestRender();
}
}, []);
for (let modelId of props.modelIds) { for (let modelId of props.modelIds) {
const streamAndPatcher = useStreamWithPatcher(ctx => ctx.attributesService.streams.get(modelId)); const streamAndPatcher = useStreamWithPatcher(ctx => ctx.attributesService.streams.get(modelId));
streamsAndPatchers.push(streamAndPatcher); streamsAndPatchers.push(streamAndPatcher);
} }
function patchAttrs(mutator) { function patchAttrs(mutator) {
View.SUPPRESS_HIGHLIGHTS = true;
for (let [model, patch] of streamsAndPatchers) { for (let [model, patch] of streamsAndPatchers) {
patch(mutator); patch(mutator);
} }
@ -69,7 +78,7 @@ export function DisplayOptionsView(props: DisplayOptionsViewProps) {
<Field active={false} name='label' onFocus={DO_NOTHING} onClick={DO_NOTHING}> <Field active={false} name='label' onFocus={DO_NOTHING} onClick={DO_NOTHING}>
<Label>Color</Label> <Label>Color</Label>
<ColorControl <ColorControl
dialogTitle={`Color for `} dialogTitle={`Color for ${props.modelIds.length} object${props.modelIds.length>0?'s':''}`}
value={attrs.color} onChange={val => patchAttrs(attrs => attrs.color = val)}/> value={attrs.color} onChange={val => patchAttrs(attrs => attrs.color = val)}/>
</Field> </Field>
</Stack>; </Stack>;

View file

@ -0,0 +1,16 @@
.root {
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
}
.label {
display: flex;
align-items: center;
}
.controls {
display: flex;
align-items: center;
}

View file

@ -0,0 +1,31 @@
import React from "react";
import {ModelButtonBehavior} from "cad/craft/ui/ModelButtonBehaviour";
import {MObject} from "cad/model/mobject";
import cx from "classnames";
import ls from './ModelButton.less'
interface ModelButtonProps {
model: MObject;
controlVisibility?: boolean;
}
export function ModelButton(props: ModelButtonProps) {
return <ModelButtonBehavior model={props.model} controlVisibility={props.controlVisibility}>
{behaviour => <div
className={cx(ls.root,
behaviour.selected&&'selected',
behaviour.highlighted&&'highlighted'
)}
onMouseEnter={behaviour.onMouseEnter}
onMouseLeave={behaviour.onMouseLeave}
onClick={behaviour.select}
>
<span className={ls.label}>
{behaviour.label}
</span>
<span className={ls.controls}>
{behaviour.controls}
</span>
</div>}
</ModelButtonBehavior>;
}

View file

@ -0,0 +1,69 @@
import React, {useContext} from 'react';
import {AppContext} from "cad/dom/components/AppContext";
import {useStream} from "ui/effects";
import {MSketchObject} from "cad/model/msketchObject";
import {VisibleSwitch} from "cad/craft/ui/SceneInlineObjectExplorer";
import {MOpenFaceShell} from "cad/model/mopenFace";
import {MObject} from "cad/model/mobject";
import {ModelIcon} from "cad/craft/ui/ModelIcon";
interface IModelButtonBehavior {
select: () => void;
selected: boolean;
highlighted: boolean;
label: any;
controls: any;
onMouseEnter: () => void;
onMouseLeave: () => void;
}
export function ModelButtonBehavior({children, model, controlVisibility}: {
children: (props:IModelButtonBehavior) => any,
model: MObject,
controlVisibility?: boolean
}) {
const ctx = useContext(AppContext);
if (controlVisibility === undefined) {
controlVisibility = !model.parent
}
const selection: string[] = useStream(ctx => ctx.streams.selection.all);
const highlights = useStream(ctx => ctx.highlightService.highlighted$);
let typeLabel = model.TYPE as string;
let idLabel = model.id;
let visibilityOf = model;
if (model instanceof MSketchObject) {
typeLabel = model.sketchPrimitive.constructor.name
} else if (model instanceof MOpenFaceShell) {
typeLabel='surface';
model = model.face;
}
const select = () => ctx.services.pickControl.pick(model);
const selected = selection.indexOf(model.id) !== -1;
const highlighted = highlights.has(model.id)
const onMouseEnter= () => ctx.highlightService.highlight(model.id);
const onMouseLeave= () => ctx.highlightService.unHighlight(model.id);
const label = <>
<ModelIcon entityType={model.TYPE} style={{marginRight: 5}} /> {typeLabel} {idLabel}
</>;
const controls = <>
{controlVisibility && <VisibleSwitch modelId={visibilityOf.id}/>}
</>;
return children({
select,
selected,
highlighted,
label,
controls,
onMouseEnter,
onMouseLeave
});
}

View file

@ -0,0 +1,33 @@
import {EntityKind} from "cad/model/entities";
import React from "react";
import {BiCubeAlt} from "react-icons/bi";
import {HiOutlineCubeTransparent} from "react-icons/hi";
import {AiOutlineRadiusSetting} from "react-icons/ai";
import {IoMdSquareOutline} from "react-icons/io";
import {GiThreePointedShuriken} from "react-icons/gi";
import {VscDebugBreakpointLogUnverified} from "react-icons/vsc";
import {FaVectorSquare} from "react-icons/fa";
import {CgArrowLongRightL, CgBorderRight} from "react-icons/cg";
export function ModelIcon(props: any) {
const {entityType, ...otherProps} = props;
const Comp = getIconComp(entityType);
return <Comp {...otherProps} />
}
function getIconComp(entityType) {
switch (entityType) {
case EntityKind.SHELL: return BiCubeAlt;
case EntityKind.EDGE: return CgBorderRight;
case EntityKind.SKETCH_OBJECT: return AiOutlineRadiusSetting;
case EntityKind.FACE: return IoMdSquareOutline;
case EntityKind.LOOP: return FaVectorSquare;
case EntityKind.VERTEX: return VscDebugBreakpointLogUnverified;
case EntityKind.DATUM: return GiThreePointedShuriken;
case EntityKind.DATUM_AXIS: return CgArrowLongRightL;
default: return HiOutlineCubeTransparent;
}
}

View file

@ -1,16 +1,16 @@
import React, {useContext, useState} from 'react'; import React, {useState} from 'react';
import {MShell} from 'cad/model/mshell'; import {MShell} from 'cad/model/mshell';
import {MDatum} from 'cad/model/mdatum'; import {MDatum} from 'cad/model/mdatum';
import {MOpenFaceShell} from "cad/model/mopenFace"; import {MOpenFaceShell} from "cad/model/mopenFace";
import {useStream, useStreamWithPatcher} from "ui/effects"; import {useStream, useStreamWithPatcher} from "ui/effects";
import {AppContext} from "cad/dom/components/AppContext";
import {MObject} from "cad/model/mobject"; import {MObject} from "cad/model/mobject";
import {SceneInlineDelineation, SceneInlineSection, SceneInlineTitleBar} from "ui/components/SceneInlineSection"; import {SceneInlineDelineation, SceneInlineSection} from "ui/components/SceneInlineSection";
import {GenericExplorerControl, GenericExplorerNode} from "ui/components/GenericExplorer"; import {GenericExplorerControl, GenericExplorerNode} from "ui/components/GenericExplorer";
import ls from "cad/craft/ui/ObjectExplorer.less"; import ls from "cad/craft/ui/ObjectExplorer.less";
import Fa from "ui/components/Fa"; import Fa from "ui/components/Fa";
import {AiOutlineEye, AiOutlineEyeInvisible} from "react-icons/ai"; import {AiOutlineEye, AiOutlineEyeInvisible} from "react-icons/ai";
import {ModelAttributes} from "cad/craft/ui/VisibleSwitch"; import {ModelButtonBehavior} from "cad/craft/ui/ModelButtonBehaviour";
import {ModelAttributes} from "cad/attributes/attributesService";
export function SceneInlineObjectExplorer() { export function SceneInlineObjectExplorer() {
@ -25,7 +25,7 @@ export function SceneInlineObjectExplorer() {
if (m instanceof MOpenFaceShell) { if (m instanceof MOpenFaceShell) {
return <OpenFaceSection shell={m} key={m.id} /> return <OpenFaceSection shell={m} key={m.id} />
} else if (m instanceof MShell) { } else if (m instanceof MShell) {
return <ModelSection type='shell' model={m} key={m.id} controlVisibility> return <ModelSection model={m} key={m.id} controlVisibility>
<Section label='faces' defaultOpen={true}> <Section label='faces' defaultOpen={true}>
{ {
m.faces.map(f => <FaceSection face={f} key={f.id}/>) m.faces.map(f => <FaceSection face={f} key={f.id}/>)
@ -37,7 +37,7 @@ export function SceneInlineObjectExplorer() {
</ModelSection> </ModelSection>
} else if (m instanceof MDatum) { } else if (m instanceof MDatum) {
return <ModelSection type='datum' model={m} key={m.id} controlVisibility/>; return <ModelSection model={m} key={m.id} controlVisibility/>;
} else { } else {
return null; return null;
} }
@ -46,13 +46,13 @@ export function SceneInlineObjectExplorer() {
} }
function EdgeSection({edge}) { function EdgeSection({edge}) {
return <ModelSection type='edge' model={edge} key={edge.id}> return <ModelSection model={edge} key={edge.id}>
{edge.adjacentFaces.map(f => <FaceSection face={f} key={f.id}/>)} {edge.adjacentFaces.map(f => <FaceSection face={f} key={f.id}/>)}
</ModelSection> </ModelSection>
} }
function FaceSection({face}) { function FaceSection({face}) {
return <ModelSection type='face' model={face} key={face.id}> return <ModelSection model={face} key={face.id}>
{(face.productionInfo && face.productionInfo.role) && {(face.productionInfo && face.productionInfo.role) &&
<Section label={<span>role: {face.productionInfo.role}</span>}/>} <Section label={<span>role: {face.productionInfo.role}</span>}/>}
@ -70,53 +70,37 @@ function SketchesList({face}) {
label={face.sketchObjects.length ? 'sketch' : <span className={ls.hint}>{'<no sketch assigned>'}</span>}> label={face.sketchObjects.length ? 'sketch' : <span className={ls.hint}>{'<no sketch assigned>'}</span>}>
{face.sketchObjects.map(o => <ModelSection {face.sketchObjects.map(o => <ModelSection
key={o.id} key={o.id}
typeLabel={o.sketchPrimitive.constructor.name}
model={o} model={o}
type={'sketchObject'}
expandable={false} expandable={false}
/>)} />)}
</Section>; </Section>;
} }
function ModelSection({model, type, typeLabel, expandable = true, controlVisibility = false, visibilityOf = null, ...props}: { export function ModelSection({model, expandable = true, controlVisibility = false, ...props}: {
model: MObject, model: MObject,
visibilityOf?: MObject,
typeLabel?: any,
type: string,
children?: any, children?: any,
controlVisibility?: boolean, controlVisibility?: boolean,
expandable?: boolean, expandable?: boolean,
}) { }) {
const ctx = useContext(AppContext);
const selection: string[] = useStream(ctx => ctx.streams.selection[type]);
const select = () => ctx.services.pickControl.pick(model); return <ModelButtonBehavior model={model} controlVisibility={controlVisibility}>
const selected = selection.indexOf(model.id) !== -1; {behavior => <GenericExplorerNode defaultExpanded={false}
let label = <>{typeLabel === undefined ? type:typeLabel} {model.id}</>;
visibilityOf = visibilityOf||model;
return <GenericExplorerNode defaultExpanded={false}
expandable={expandable} expandable={expandable}
label={label} label={behavior.label}
selected={selected} selected={behavior.selected}
select={select} select={behavior.select}
onMouseEnter={() => ctx.highlightService.highlight(model.id)} highlighted={behavior.highlighted}
onMouseLeave={() => ctx.highlightService.unHighlight(model.id)} onMouseEnter={behavior.onMouseEnter}
controls={ onMouseLeave={behavior.onMouseLeave}
<> controls={behavior.controls}>
{controlVisibility && <VisibleSwitch modelId={visibilityOf.id}/>}
</>
}>
{props.children} {props.children}
</GenericExplorerNode> </GenericExplorerNode>}
</ModelButtonBehavior>;
} }
function OpenFaceSection({shell}) { function OpenFaceSection({shell}) {
return <ModelSection type='face' model={shell.face} key={shell.face.id} typeLabel='surface' return <ModelSection model={shell} key={shell.id} controlVisibility>
controlVisibility
visibilityOf={shell}>
<SketchesList face={shell.face}/> <SketchesList face={shell.face}/>
</ModelSection>; </ModelSection>;
} }
@ -142,13 +126,15 @@ export function VisibleSwitch({modelId}) {
let [attrs, patch] = useStreamWithPatcher<ModelAttributes>(ctx => ctx.attributesService.streams.get(modelId)); let [attrs, patch] = useStreamWithPatcher<ModelAttributes>(ctx => ctx.attributesService.streams.get(modelId));
const onClick = () => { const onClick = (e) => {
patch(attr => { patch(attr => {
attr.hidden = !attr.hidden attr.hidden = !attr.hidden
}) });
e.stopPropagation();
return false;
} }
return <GenericExplorerControl onClick={onClick} title={'test'} on={attrs.hidden}> return <GenericExplorerControl onClick={onClick} title={attrs.hidden ? 'show' : 'hide'} on={attrs.hidden}>
{attrs.hidden ? <AiOutlineEyeInvisible /> : <AiOutlineEye />} {attrs.hidden ? <AiOutlineEyeInvisible /> : <AiOutlineEye />}
</GenericExplorerControl> </GenericExplorerControl>
} }

View file

@ -8,6 +8,8 @@ export interface DomService {
contributeComponent: (comp: () => JSX.Element) => void contributeComponent: (comp: () => JSX.Element) => void
setCursor(cursor: string);
} }
interface DomPluginInputContext { interface DomPluginInputContext {
@ -39,7 +41,14 @@ export const DomPlugin: Plugin<DomPluginInputContext, DomPluginContext, DomPlugi
activate(ctx: DomPluginInputContext&DomPluginContext) { activate(ctx: DomPluginInputContext&DomPluginContext) {
ctx.domService = { ctx.domService = {
viewerContainer: document.getElementById('viewer-container'), viewerContainer: document.getElementById('viewer-container'),
contributeComponent contributeComponent,
setCursor(cursor: string) {
if (cursor) {
ctx.domService.viewerContainer.style.cursor = cursor;
} else {
ctx.domService.viewerContainer.style.removeProperty('cursor');
}
}
}; };
ctx.services.dom = ctx.domService; ctx.services.dom = ctx.domService;

View file

@ -0,0 +1,48 @@
import React from "react";
import {state} from "lstream";
import {useStreamWithUpdater} from "ui/effects";
import Window from "ui/components/Window";
import {MObject} from "cad/model/mobject";
import Stack from "ui/components/Stack";
import {ModelSection} from "cad/craft/ui/SceneInlineObjectExplorer";
import {ModelButton} from "cad/craft/ui/ModelButton";
export interface PickListDialogRequest {
x: number;
y: number;
token: any;
capture: MObject[]
}
export const PickListDialogRequest$ = state<PickListDialogRequest>(null);
export function PickListDialog() {
const [req, setReq] = useStreamWithUpdater(() => PickListDialogRequest$);
const close = () => setReq(null);
if (!req) {
return null;
}
return <Window key={req.token}
initWidth={250}
initLeft={req.x}
initTop={req.y}
title='pick list'
className='small-typography'
onClose={close}>
<Stack>
{req.capture.map(model => {
return <ModelButton
key={model.id}
model={model}
/>
})}
</Stack>
</Window>
}

View file

@ -1,8 +1,13 @@
import * as mask from 'gems/mask' import * as mask from 'gems/mask'
import {getAttribute} from 'scene/objectData'; import {getAttribute} from 'scene/objectData';
import {FACE, EDGE, SKETCH_OBJECT, DATUM, SHELL, DATUM_AXIS, LOOP} from '../../model/entities'; import {DATUM, DATUM_AXIS, EDGE, FACE, LOOP, SHELL, SKETCH_OBJECT} from '../../model/entities';
import {LOG_FLAGS} from 'cad/logFlags'; import {LOG_FLAGS} from 'cad/logFlags';
import {initRayCastDebug, printRaycastDebugInfo, RayCastDebugInfo} from "./rayCastDebug"; import {initRayCastDebug, printRaycastDebugInfo, RayCastDebugInfo} from "./rayCastDebug";
import {PickListDialog, PickListDialogRequest$} from "cad/scene/controls/PickListDialog";
import {contributeComponent} from "cad/dom/components/ContributedComponents";
import {MObject} from "cad/model/mobject";
import {MFace} from "cad/model/mface";
import {MOpenFaceShell} from "cad/model/mopenFace";
export interface PickControlService { export interface PickControlService {
setPickHandler(wizardPickHandler: (model) => boolean) setPickHandler(wizardPickHandler: (model) => boolean)
@ -40,10 +45,12 @@ const DEFAULT_SELECTION_MODE = Object.freeze({
export const ALL_EXCLUDING_SOLID_KINDS = PICK_KIND.FACE | PICK_KIND.SKETCH | PICK_KIND.EDGE | PICK_KIND.DATUM_AXIS | PICK_KIND.LOOP; export const ALL_EXCLUDING_SOLID_KINDS = PICK_KIND.FACE | PICK_KIND.SKETCH | PICK_KIND.EDGE | PICK_KIND.DATUM_AXIS | PICK_KIND.LOOP;
export const ALL_POSSIBLE_KIND = Number.MAX_SAFE_INTEGER;
export function activate(context) { export function activate(context) {
const {services} = context; const {services} = context;
context.domService.contributeComponent(PickListDialog);
const defaultHandler = (model, event, rayCastData?) => { const defaultHandler = (model, event, rayCastData?) => {
if (LOG_FLAGS.PICK) { if (LOG_FLAGS.PICK) {
printPickInfo(model, rayCastData); printPickInfo(model, rayCastData);
@ -62,6 +69,10 @@ export function activate(context) {
return false; return false;
} }
} }
} else if (type === SHELL) {
if (dispatchSelection(SHELL, modelId, event)) {
return false;
}
} else if (type === SKETCH_OBJECT) { } else if (type === SKETCH_OBJECT) {
if (dispatchSelection(SKETCH_OBJECT, modelId, event)) { if (dispatchSelection(SKETCH_OBJECT, modelId, event)) {
return false; return false;
@ -85,12 +96,30 @@ export function activate(context) {
domElement.addEventListener('mousedown', mousedown, false); domElement.addEventListener('mousedown', mousedown, false);
domElement.addEventListener('mouseup', mouseup, false); domElement.addEventListener('mouseup', mouseup, false);
domElement.addEventListener('dblclick', mousedblclick, false); domElement.addEventListener('dblclick', mousedblclick, false);
domElement.addEventListener('mousemove', mousemove, false);
let mouseState = { let mouseState = {
startX: 0, startX: 0,
startY: 0 startY: 0
}; };
let timeoutId = null;
let pickListDialogMode = false;
function mousemove(e) {
if (pickListDialogMode) {
context.domService.setCursor(null);
pickListDialogMode = false;
}
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(() => {
pickListDialogMode = true;
context.domService.setCursor('crosshair');
}, 500);
}
function mousedown(e) { function mousedown(e) {
mouseState.startX = e.offsetX; mouseState.startX = e.offsetX;
mouseState.startY = e.offsetY; mouseState.startY = e.offsetY;
@ -122,6 +151,25 @@ export function activate(context) {
function handlePick(event) { function handlePick(event) {
let pickResults = services.viewer.raycast(event, services.cadScene.workGroup.children, RayCastDebugInfo); let pickResults = services.viewer.raycast(event, services.cadScene.workGroup.children, RayCastDebugInfo);
if (pickListDialogMode) {
const capture = new Set<MObject>();
traversePickResults(event, pickResults, ALL_POSSIBLE_KIND, (model) => {
if (!(model.parent instanceof MOpenFaceShell)) {
capture.add(model);
}
if (model instanceof MFace) {
capture.add(model.shell);
}
return true;
});
PickListDialogRequest$.next({
x: event.offsetX,
y: event.offsetY,
token: Date.now(),
capture: Array.from(capture)
});
return;
}
traversePickResults(event, pickResults, ALL_EXCLUDING_SOLID_KINDS, pickHandler); traversePickResults(event, pickResults, ALL_EXCLUDING_SOLID_KINDS, pickHandler);
} }
@ -141,7 +189,8 @@ export function activate(context) {
function dispatchSelection(entityType, selectee, event) { function dispatchSelection(entityType, selectee, event) {
let marker = services.marker; let marker = services.marker;
if (marker.isMarked(selectee)) { if (marker.isMarked(selectee)) {
return false; marker.withdraw(selectee);
return true;
} }
let multiMode = event && event.shiftKey; let multiMode = event && event.shiftKey;
@ -158,7 +207,7 @@ export function activate(context) {
traversePickResults(e, pickResults, PICK_KIND.FACE, (sketchFace) => { traversePickResults(e, pickResults, PICK_KIND.FACE, (sketchFace) => {
const shell = sketchFace.shell; const shell = sketchFace.shell;
services.marker.markExclusively(shell.TYPE, shell.id); services.marker.markExclusively(shell.TYPE, shell.id);
context.locationService.edit(shell); // context.locationService.edit(shell);
return false; return false;
}); });
} }

View file

@ -1,16 +1,36 @@
import {Plugin} from "plugable/pluginSystem"; import {Plugin} from "plugable/pluginSystem";
import {combine, stream} from "lstream"; import {combine, merge, Stream, stream} from "lstream";
import Viewer from "cad/scene/viewer"; import Viewer from "cad/scene/viewer";
import {ScanStream} from "lstream/scan";
export class HighlightService { export class HighlightService {
highlightEvents = stream<string>(); highlightEvents = stream<string>();
unHighlightEvents = stream<string>(); unHighlightEvents = stream<string>();
highlighted$: Stream<Set<string>>;
constructor(viewer: Viewer) { constructor(viewer: Viewer) {
combine(this.highlightEvents, this.unHighlightEvents) combine(this.highlightEvents, this.unHighlightEvents)
.throttle() .throttle()
.attach(() => viewer.requestRender()) .attach(() => viewer.requestRender())
this.highlighted$ = merge(
this.highlightEvents.map(id => ({type: '+', id})),
this.unHighlightEvents.map(id => ({type: '-', id})),
).scan(new Set<string>(), (highlight, event) => {
switch (event.type) {
case '+': {
highlight.add(event.id);
break;
}
case '-': {
highlight.delete(event.id);
break;
}
default: throw 'illegal state'
}
return highlight;
})
} }
highlight(id: string) { highlight(id: string) {

View file

@ -9,6 +9,8 @@ export interface MarkerService {
mark(id: any, color: any) mark(id: any, color: any)
withdraw(id: any);
commit() commit()
markExclusively() markExclusively()
@ -48,7 +50,7 @@ function createMarker(findEntity, requestRender) {
function doMark(id, color) { function doMark(id, color) {
let mObj = findEntity(id); let mObj = findEntity(id);
if (!mObj) { if (!mObj) {
console.warn('no entity found to highlight: ' + id); console.warn('no entity found to select: ' + id);
return; return;
} }
marked.set(id, mObj); marked.set(id, mObj);
@ -60,6 +62,16 @@ function createMarker(findEntity, requestRender) {
obj.ext.view && obj.ext.view.withdraw('selection'); obj.ext.view && obj.ext.view.withdraw('selection');
} }
function withdraw(id) {
let mObj = findEntity(id);
if (!mObj) {
console.warn('no entity found to deselect: ' + id);
return;
}
doWithdraw(mObj);
onUpdate();
}
function onUpdate() { function onUpdate() {
requestRender(); requestRender();
notify(); notify();
@ -134,7 +146,8 @@ function createMarker(findEntity, requestRender) {
} }
return { return {
clear, startSession, mark, commit, markExclusively, markArrayExclusively, markAdding, isMarked, $markedEntities clear, startSession, mark, commit, markExclusively, markArrayExclusively, markAdding, isMarked, $markedEntities,
withdraw
}; };
} }

View file

@ -38,10 +38,6 @@ export class SketchingView extends View {
}); });
} }
setColor(color) {
this.color = color;
}
updateVisuals() { updateVisuals() {
this.mesh.material.color.set(this.markColor||this.parent.markColor||this.color||this.parent.color||NULL_COLOR); this.mesh.material.color.set(this.markColor||this.parent.markColor||this.color||this.parent.color||NULL_COLOR);
} }

View file

@ -60,9 +60,9 @@ export class ShellView extends View {
this.vertexViews.forEach(e => e.traverse(visitor)); this.vertexViews.forEach(e => e.traverse(visitor));
} }
updateVisuals(color) { updateVisuals() {
super.updateVisuals(color); super.updateVisuals();
this.faceViews.forEach(f => f.updateVisuals(color)); this.faceViews.forEach(f => f.updateVisuals());
} }
dispose() { dispose() {

View file

@ -14,9 +14,10 @@ const MarkerTable = [
}, },
]; ];
export class View { export class View {
static SUPPRESS_HIGHLIGHTS = false
static MARKER = 'ModelView'; static MARKER = 'ModelView';
disposers = createFunctionList(); disposers = createFunctionList();
@ -31,9 +32,14 @@ export class View {
} }
setColor(color) { setColor(color) {
this.color = color;
this.updateVisuals();
} }
get markColor() { get markColor() {
if (View.SUPPRESS_HIGHLIGHTS) {
return null;
}
if (this.marks.length !== 0) { if (this.marks.length !== 0) {
const baseMark = this.marks[0]; const baseMark = this.marks[0];
return baseMark.colors[Math.min(baseMark.colors.length, this.marks.length) - 1]; return baseMark.colors[Math.min(baseMark.colors.length, this.marks.length) - 1];

View file

@ -48,7 +48,7 @@ export class SceneSolid {
export function createSolidMaterial(skin) { export function createSolidMaterial(skin) {
return new THREE.MeshPhongMaterial(Object.assign({ return new THREE.MeshPhongMaterial(Object.assign({
vertexColors: THREE.FaceColors, vertexColors: THREE.FaceColors,
// color: 0xB0C4DE, color: 0xaeaeae,
shininess: 0, shininess: 0,
polygonOffset : true, polygonOffset : true,
polygonOffsetFactor : 1, polygonOffsetFactor : 1,