mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-07 08:53:25 +01:00
wireframe/shaded render modes
This commit is contained in:
parent
ed5b2a41da
commit
68fe161e30
12 changed files with 118 additions and 73 deletions
|
|
@ -77,7 +77,6 @@ export function CADTrackballControls( object, domElement ) {
|
|||
|
||||
// events
|
||||
|
||||
var changeEvent = { type: 'change' };
|
||||
var startEvent = { type: 'start' };
|
||||
var endEvent = { type: 'end' };
|
||||
|
||||
|
|
@ -304,7 +303,7 @@ export function CADTrackballControls( object, domElement ) {
|
|||
|
||||
};
|
||||
|
||||
this.update = function () {
|
||||
this.evaluate = function () {
|
||||
|
||||
_eye.subVectors( _this.object.position, _this.target );
|
||||
|
||||
|
|
@ -332,16 +331,12 @@ export function CADTrackballControls( object, domElement ) {
|
|||
|
||||
_this.object.lookAt( _this.target );
|
||||
|
||||
if ( lastPosition.distanceToSquared( _this.object.position ) > EPS || this.projectionChanged) {
|
||||
|
||||
const needsRender = lastPosition.distanceToSquared( _this.object.position ) > EPS || this.projectionChanged;
|
||||
if ( needsRender) {
|
||||
this.projectionChanged = false;
|
||||
|
||||
_this.dispatchEvent( changeEvent );
|
||||
|
||||
lastPosition.copy( _this.object.position );
|
||||
|
||||
}
|
||||
|
||||
return needsRender;
|
||||
};
|
||||
|
||||
this.reset = function () {
|
||||
|
|
@ -357,8 +352,6 @@ export function CADTrackballControls( object, domElement ) {
|
|||
|
||||
_this.object.lookAt( _this.target );
|
||||
|
||||
_this.dispatchEvent( changeEvent );
|
||||
|
||||
lastPosition.copy( _this.object.position );
|
||||
|
||||
};
|
||||
|
|
@ -656,11 +649,7 @@ export function CADTrackballControls( object, domElement ) {
|
|||
window.addEventListener( 'keyup', keyup, false );
|
||||
|
||||
this.handleResize();
|
||||
|
||||
// force an update at start
|
||||
this.update();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
CADTrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
|
||||
CADTrackballControls.prototype.constructor = CADTrackballControls;
|
||||
|
|
@ -16,7 +16,6 @@ import {
|
|||
Vector3,
|
||||
WebGLRenderer
|
||||
} from "three";
|
||||
import {TransformControls} from "three/examples/jsm/controls/TransformControls";
|
||||
import {stream} from "lstream";
|
||||
|
||||
export default class SceneSetUp {
|
||||
|
|
@ -33,10 +32,9 @@ export default class SceneSetUp {
|
|||
private _prevContainerWidth: number;
|
||||
private _prevContainerHeight: number;
|
||||
trackballControls: CADTrackballControls;
|
||||
transformControls: TransformControls;
|
||||
updateControlsAndHelpers: () => void;
|
||||
viewportSizeUpdate$ = stream();
|
||||
|
||||
renderRequested: boolean;
|
||||
|
||||
constructor(container, onRendered) {
|
||||
|
||||
this.workingSphere = 10000;
|
||||
|
|
@ -45,7 +43,8 @@ export default class SceneSetUp {
|
|||
this.rootGroup = this.scene;
|
||||
this.onRendered = onRendered;
|
||||
this.scene.userData.sceneSetUp = this;
|
||||
|
||||
this.renderRequested = false;
|
||||
|
||||
this.setUpCamerasAndLights();
|
||||
this.setUpControls();
|
||||
|
||||
|
|
@ -55,7 +54,11 @@ export default class SceneSetUp {
|
|||
aspect() {
|
||||
return this.container.clientWidth / this.container.clientHeight;
|
||||
}
|
||||
|
||||
|
||||
requestRender() {
|
||||
this.renderRequested = true;
|
||||
}
|
||||
|
||||
createOrthographicCamera() {
|
||||
let width = this.container.clientWidth;
|
||||
let height = this.container.clientHeight;
|
||||
|
|
@ -101,7 +104,7 @@ export default class SceneSetUp {
|
|||
this.updateOrthographicCameraViewport();
|
||||
this.renderer.setSize( this.container.clientWidth, this.container.clientHeight );
|
||||
this.viewportSizeUpdate$.next();
|
||||
this.render();
|
||||
this.__render_NeverCallMeFromOutside();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -148,8 +151,7 @@ export default class SceneSetUp {
|
|||
|
||||
this.camera = camera;
|
||||
this.trackballControls.object = camera;
|
||||
this.transformControls.camera = camera;
|
||||
this.updateControlsAndHelpers();
|
||||
this.requestRender();
|
||||
}
|
||||
|
||||
setUpControls() {
|
||||
|
|
@ -173,29 +175,7 @@ export default class SceneSetUp {
|
|||
trackballControls.dynamicDampingFactor = 0.3;
|
||||
|
||||
trackballControls.keys = [ 65, 83, 68 ];
|
||||
trackballControls.addEventListener( 'change', () => this.render());
|
||||
|
||||
let transformControls: any = new TransformControls( this.camera, this.renderer.domElement );
|
||||
transformControls.addEventListener( 'change', () => this.render() );
|
||||
this.scene.add( transformControls );
|
||||
|
||||
this.trackballControls = trackballControls;
|
||||
this.transformControls = transformControls;
|
||||
|
||||
let updateTransformControls = () => {
|
||||
if (transformControls.object !== undefined) {
|
||||
if (transformControls.object.parent === undefined) {
|
||||
transformControls.detach();
|
||||
this.render();
|
||||
}
|
||||
transformControls.update();
|
||||
}
|
||||
};
|
||||
|
||||
this.updateControlsAndHelpers = function() {
|
||||
trackballControls.update();
|
||||
updateTransformControls();
|
||||
};
|
||||
}
|
||||
|
||||
createRaycaster(viewX, viewY) {
|
||||
|
|
@ -288,11 +268,15 @@ export default class SceneSetUp {
|
|||
|
||||
animate() {
|
||||
requestAnimationFrame( () => this.animate() );
|
||||
this.updateControlsAndHelpers();
|
||||
const controlsChangedViewpoint = this.trackballControls.evaluate();
|
||||
if (controlsChangedViewpoint || this.renderRequested) {
|
||||
this.__render_NeverCallMeFromOutside();
|
||||
}
|
||||
this.updateViewportSizeIfNeeded();
|
||||
};
|
||||
|
||||
render() {
|
||||
private __render_NeverCallMeFromOutside() {
|
||||
this.renderRequested = false;
|
||||
this.light.position.set(this.camera.position.x, this.camera.position.y, this.camera.position.z);
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
this.onRendered();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import Vector, {AXIS, ORIGIN} from 'math/vector';
|
||||
import {RiCamera2Line} from "react-icons/ri";
|
||||
import {ViewMode} from "cad/scene/viewer";
|
||||
import {GiCube, HiCube, HiOutlineCube} from "react-icons/all";
|
||||
|
||||
const NEG_X = AXIS.X.negate();
|
||||
const NEG_Y = AXIS.Y.negate();
|
||||
|
|
@ -192,4 +194,37 @@ export default [
|
|||
noWizardFocus: true
|
||||
})
|
||||
},
|
||||
{
|
||||
id: 'ViewMode_WIREFRAME_ON',
|
||||
appearance: {
|
||||
label: 'wireframe',
|
||||
icon: HiOutlineCube,
|
||||
},
|
||||
invoke: ctx => {
|
||||
ctx.services.viewer.viewMode$.next(ViewMode.WIREFRAME);
|
||||
ctx.services.viewer.requestRender();
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'ViewMode_SHADED_ON',
|
||||
appearance: {
|
||||
label: 'shaded',
|
||||
icon: HiCube,
|
||||
},
|
||||
invoke: ctx => {
|
||||
ctx.services.viewer.viewMode$.next(ViewMode.SHADED);
|
||||
ctx.services.viewer.requestRender();
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'ViewMode_SHADED_WITH_EDGES_ON',
|
||||
appearance: {
|
||||
label: 'shaded with edges',
|
||||
icon: GiCube,
|
||||
},
|
||||
invoke: ctx => {
|
||||
ctx.services.viewer.viewMode$.next(ViewMode.SHADED_WITH_EDGES);
|
||||
ctx.services.viewer.requestRender();
|
||||
}
|
||||
},
|
||||
]
|
||||
|
|
@ -136,7 +136,7 @@ export interface OperationDescriptor<R> {
|
|||
previewGeomProvider?: (params: R) => OperationGeometryProvider,
|
||||
previewer?: any,
|
||||
form: FormDefinition | React.FunctionComponent,
|
||||
defaultActiveField: string,
|
||||
defaultActiveField?: string,
|
||||
schema?: OperationSchema,
|
||||
onParamsUpdate?: (params, name, value) => void,
|
||||
masking?: {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
.left, .right {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.right {
|
||||
|
|
@ -30,6 +30,8 @@
|
|||
@border: 1px solid @border-color;
|
||||
.left .button {
|
||||
border-right: @border;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.right .button {
|
||||
border-left: @border;
|
||||
|
|
|
|||
|
|
@ -23,17 +23,21 @@ function ButtonGroup({actions}) {
|
|||
class ActionButton extends React.Component {
|
||||
|
||||
render() {
|
||||
let {label, cssIcons, enabled, visible, actionId, ...props} = this.props;
|
||||
let {label, cssIcons, icon, enabled, visible, actionId, ...props} = this.props;
|
||||
if (!visible) {
|
||||
return null;
|
||||
}
|
||||
const Icon = icon ? icon : null;
|
||||
|
||||
if (isMenuAction(actionId)) {
|
||||
let onClick = props.onClick;
|
||||
props.onClick = e => onClick(menuAboveElementHint(this.el));
|
||||
}
|
||||
|
||||
return <ControlBarButton disabled={!enabled} onElement={el => this.el = el} {...props} >
|
||||
{cssIcons && <Fa fa={cssIcons} fw/>} {label}
|
||||
{cssIcons && <Fa fa={cssIcons} fw/>}
|
||||
{Icon && <Icon />}
|
||||
{label}
|
||||
</ControlBarButton>;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,18 @@
|
|||
import {printRaycastDebugInfo, RayCastDebugInfo} from "./rayCastDebug";
|
||||
import {LOG_FLAGS} from "../../logFlags";
|
||||
import {LOG_FLAGS} from "cad/logFlags";
|
||||
import {stream} from "lstream";
|
||||
|
||||
const MouseStates = {
|
||||
IDLE: 'IDLE',
|
||||
DOWN: 'DOWN'
|
||||
}
|
||||
|
||||
export function activate(ctx) {
|
||||
const {services, streams} = ctx;
|
||||
const domElement = services.viewer.sceneSetup.domElement();
|
||||
const event = {
|
||||
viewer: services.viewer
|
||||
viewer: services.viewer,
|
||||
mouseState: MouseStates.IDLE
|
||||
};
|
||||
|
||||
domElement.addEventListener('mousedown', mousedown, false);
|
||||
|
|
@ -13,6 +20,12 @@ export function activate(ctx) {
|
|||
domElement.addEventListener('mousemove', mousemove, false);
|
||||
domElement.addEventListener('dblclick', dblclick, false);
|
||||
|
||||
const onMoveLogicRequest$ = stream();
|
||||
|
||||
onMoveLogicRequest$.throttle(100).attach(() => {
|
||||
const hits = performRaycast(event.mouseEvent);
|
||||
dispatchMousemove(event.mouseEvent, hits)
|
||||
});
|
||||
|
||||
let performRaycast = e => {
|
||||
const hits = services.viewer.raycast(e, services.cadScene.workGroup.children, RayCastDebugInfo);
|
||||
|
|
@ -43,6 +56,7 @@ export function activate(ctx) {
|
|||
}
|
||||
|
||||
function mousedown(e) {
|
||||
event.mouseState = MouseStates.DOWN;
|
||||
let hits = performRaycast(e);
|
||||
dispatchMousedown(e, hits);
|
||||
}
|
||||
|
|
@ -69,6 +83,7 @@ export function activate(ctx) {
|
|||
}
|
||||
|
||||
function mouseup(e) {
|
||||
event.mouseState = MouseStates.IDLE;
|
||||
event.mouseEvent = e;
|
||||
if (toDrag) {
|
||||
stopDrag(e);
|
||||
|
|
@ -111,8 +126,9 @@ export function activate(ctx) {
|
|||
if (toDrag) {
|
||||
toDrag.dragMove(event);
|
||||
} else {
|
||||
let hits = performRaycast(e);
|
||||
dispatchMousemove(e, hits)
|
||||
if (event.mouseState === MouseStates.IDLE) {
|
||||
onMoveLogicRequest$.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,35 +1,33 @@
|
|||
import SceneSetup from 'scene/sceneSetup';
|
||||
import {Emitter, externalState, StateStream, stream} from "lstream";
|
||||
import {Emitter, externalState, state, StateStream, stream} from "lstream";
|
||||
import SceneSetUp from "scene/sceneSetup";
|
||||
|
||||
export enum ViewMode {
|
||||
WIREFRAME = 'WIREFRAME',
|
||||
SHADED = 'SHADED',
|
||||
SHADED_WITH_EDGES = 'SHADED_WITH_EDGES'
|
||||
}
|
||||
|
||||
export default class Viewer {
|
||||
|
||||
sceneRendered$: Emitter<any> = stream();
|
||||
cameraMode$: StateStream<any>;
|
||||
sceneSetup: SceneSetUp;
|
||||
renderRequested: boolean;
|
||||
viewMode$: StateStream<ViewMode> = state(ViewMode.SHADED_WITH_EDGES);
|
||||
|
||||
sceneSetup: SceneSetUp;
|
||||
|
||||
constructor(container, onRendered) {
|
||||
|
||||
this.cameraMode$ = externalState(() => this.getCameraMode(), mode => this.setCameraMode(mode))
|
||||
|
||||
this.sceneSetup = new SceneSetup(container, onRendered);
|
||||
this.renderRequested = false;
|
||||
this.sceneSetup = new SceneSetUp(container, onRendered);
|
||||
}
|
||||
|
||||
render() {
|
||||
this.sceneSetup.render();
|
||||
this.requestRender();
|
||||
}
|
||||
|
||||
requestRender = () => {
|
||||
if (this.renderRequested) {
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.renderRequested = false;
|
||||
this.render();
|
||||
});
|
||||
this.sceneSetup.requestRender();
|
||||
};
|
||||
|
||||
setVisualProp = (obj, prop, value) => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import {CurveBasedView} from './curveBasedView';
|
||||
import {ViewMode} from "cad/scene/viewer";
|
||||
|
||||
const MarkerTable = [
|
||||
{
|
||||
|
|
@ -19,5 +20,8 @@ export class EdgeView extends CurveBasedView {
|
|||
let brepEdge = edge.brepEdge;
|
||||
let tess = brepEdge.data.tessellation ? brepEdge.data.tessellation : brepEdge.curve.tessellateToData();
|
||||
super(ctx, edge, tess, 3, 0x000000, MarkerTable);
|
||||
this.addDisposer(ctx.viewer.viewMode$.attach(mode => {
|
||||
this.representation.visible = (mode === ViewMode.SHADED_WITH_EDGES || mode === ViewMode.WIREFRAME);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {createSolidMaterial} from "cad/scene/views/viewUtils";
|
|||
import {SketchMesh} from "cad/scene/views/shellView";
|
||||
import {FACE} from "cad/model/entities";
|
||||
import {setAttribute} from "scene/objectData";
|
||||
import {ViewMode} from "cad/scene/viewer";
|
||||
|
||||
export class SketchingView extends View {
|
||||
|
||||
|
|
@ -69,7 +70,6 @@ export class FaceView extends SketchingView {
|
|||
super(ctx, face, parent);
|
||||
let geom;
|
||||
|
||||
this.meshFaces = [];
|
||||
if (face.brepFace.data.tessellation) {
|
||||
geom = tessDataToGeom(face.brepFace.data.tessellation.data)
|
||||
} else {
|
||||
|
|
@ -86,6 +86,10 @@ export class FaceView extends SketchingView {
|
|||
this.ctx.highlightService.unHighlight(this.model.id);
|
||||
}
|
||||
this.rootGroup.add(this.mesh);
|
||||
|
||||
this.addDisposer(ctx.viewer.viewMode$.attach(mode => {
|
||||
this.mesh.visible = (mode === ViewMode.SHADED_WITH_EDGES || mode === ViewMode.SHADED);
|
||||
}));
|
||||
}
|
||||
|
||||
dispose() {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import {GiCube} from "react-icons/all";
|
||||
|
||||
export default [
|
||||
{
|
||||
id: 'file',
|
||||
|
|
@ -26,6 +28,13 @@ export default [
|
|||
actions: ['StandardViewFront', 'StandardViewBack', 'StandardViewLeft', 'StandardViewRight',
|
||||
'StandardViewTop', 'StandardViewBottom', 'StandardView3Way']
|
||||
},
|
||||
{
|
||||
id: 'viewModes',
|
||||
label: 'mode',
|
||||
icon: GiCube,
|
||||
info: 'view/render mode',
|
||||
actions: ['ViewMode_WIREFRAME_ON', 'ViewMode_SHADED_ON', 'ViewMode_SHADED_WITH_EDGES_ON']
|
||||
},
|
||||
{
|
||||
id: 'boolean',
|
||||
label: 'bool',
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import {Explorer} from "cad/dom/components/Explorer";
|
|||
|
||||
export function activate(ctx) {
|
||||
const {services, streams} = ctx;
|
||||
streams.ui.controlBars.left.value = ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'menu.views', 'Donate', 'GitHub'];
|
||||
streams.ui.controlBars.left.value = ['menu.file', 'menu.craft', 'menu.boolean', 'menu.primitives', 'menu.views', 'menu.viewModes', 'Donate', 'GitHub'];
|
||||
streams.ui.controlBars.right.value = [
|
||||
['Info', {label: null}],
|
||||
['RefreshSketches', {label: null}],
|
||||
|
|
|
|||
Loading…
Reference in a new issue