<commit_before>/* eslint-disable import/no-unresolved */
import 'imports-loader?THREE=three!three/examples/js/cameras/CombinedCamera';
/* eslint-enable */
import _ from 'lodash';
import colornames from 'colornames';
import pubsub from 'pubsub-js';
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import * as THREE from 'three';
import Detector from 'three/examples/js/Detector';
import log from '../../lib/log';
import './TrackballControls';
import { getBoundingBox, loadTexture } from './helpers';
import Viewport from './Viewport';
import CoordinateAxes from './CoordinateAxes';
import ToolHead from './ToolHead';
import TargetPoint from './TargetPoint';
import GridLine from './GridLine';
import PivotPoint3 from './PivotPoint3';
import TextSprite from './TextSprite';
import GCodeVisualizer from './GCodeVisualizer';
import {
    IMPERIAL_UNITS,
    METRIC_UNITS
} from '../../constants';
import {
    CAMERA_MODE_PAN,
    CAMERA_MODE_ROTATE
} from './constants';

const IMPERIAL_GRID_COUNT = 32; // 32 in
const IMPERIAL_GRID_SPACING = 25.4; // 1 in
const IMPERIAL_AXIS_LENGTH = IMPERIAL_GRID_SPACING * 12; // 12 in
const METRIC_GRID_COUNT = 60; // 60 cm
const METRIC_GRID_SPACING = 10; // 10 mm
const METRIC_AXIS_LENGTH = METRIC_GRID_SPACING * 30; // 300 mm
const CAMERA_VIEWPORT_WIDTH = 300; // 300 mm
const CAMERA_VIEWPORT_HEIGHT = 300; // 300 mm
const PERSPECTIVE_FOV = 70;
const PERSPECTIVE_NEAR = 0.001;
const PERSPECTIVE_FAR = 2000;
const ORTHOGRAPHIC_FOV = 35;
const ORTHOGRAPHIC_NEAR = 0.001;
const ORTHOGRAPHIC_FAR = 2000;
const CAMERA_POSITION_X = 0;
const CAMERA_POSITION_Y = 0;
const CAMERA_POSITION_Z = 200; // Move the camera out a bit from the origin (0, 0, 0)
const TRACKBALL_CONTROLS_MIN_DISTANCE = 1;
const TRACKBALL_CONTROLS_MAX_DISTANCE = 2000;

class Visualizer extends Component {
    static propTypes = {
        show: PropTypes.bool,
        state: PropTypes.object
    };

    pubsubTokens = [];
    isAgitated = false;
    workPosition = {
        x: 0,
        y: 0,
        z: 0
    };
    group = new THREE.Group();
    pivotPoint = new PivotPoint3({ x: 0, y: 0, z: 0 }, (x, y, z) => { // relative position
        _.each(this.group.children, (o) => {
            o.translateX(x);
            o.translateY(y);
            o.translateZ(z);
        });

        // Update the scene
        this.updateScene();
    });

    componentWillMount() {
        // Three.js
        this.renderer = null;
        this.scene = null;
        this.camera = null;
        this.controls = null;
        this.viewport = null;
        this.toolhead = null;
        this.targetPoint = null;
        this.visualizer = null;
    }
    componentDidMount() {
        this.subscribe();
        this.addResizeEventListener();

        if (this.node) {
            const el = ReactDOM.findDOMNode(this.node);
            this.createScene(el);
            this.resizeRenderer();
        }
    }
    componentWillUnmount() {
        this.unsubscribe();
        this.removeResizeEventListener();
        this.clearScene();
    }
    componentWillReceiveProps(nextProps) {
        let forceUpdate = false;
        let needUpdateScene = false;
        const state = this.props.state;
        const nextState = nextProps.state;

        // Enable or disable 3D view
        if ((this.props.show !== nextProps.show) && (!!nextProps.show === true)) {
            this.viewport.update();

            // Set forceUpdate to true when enabling or disabling 3D view
            forceUpdate = true;
            needUpdateScene = true;
        }

        // Update visualizer's frame index
        if (this.visualizer) {
            const frameIndex = nextState.gcode.sent;
            this.visualizer.setFrameIndex(frameIndex);
        }

        // Projection
        if (state.projection !== nextState.projection) {
            if (nextState.projection === 'orthographic') {
                this.camera.toOrthographic();
                this.camera.setZoom(1);
                this.camera.setFov(ORTHOGRAPHIC_FOV);
            } else {
                this.camera.toPerspective();
                this.camera.setZoom(1);
                this.camera.setFov(PERSPECTIVE_FOV);
            }
            if (this.viewport) {
                this.viewport.update();
            }
            needUpdateScene = true;
        }

        // Camera Mode
        if (state.cameraMode !== nextState.cameraMode) {
            this.setCameraMode(nextState.cameraMode);
            needUpdateScene = true;
        }

        // Display the name of the G-code file
        if (state.gcode.displayName !== nextState.gcode.displayName) {
            const gcodeDisplayName = this.group.getObjectByName('GCodeDisplayName');
            if (gcodeDisplayName) {
                gcodeDisplayName.visible = nextState.gcode.displayName;

                needUpdateScene = true;
            }
        }

        // Display or hide coordinate system
        if ((nextState.units !== state.units) ||
            (nextState.objects.coordinateSystem.visible !== state.objects.coordinateSystem.visible)) {
            const visible = nextState.objects.coordinateSystem.visible;

            // Imperial
            const imperialCoordinateSystem = this.group.getObjectByName('ImperialCoordinateSystem');
            if (imperialCoordinateSystem) {
                imperialCoordinateSystem.visible = visible && (nextState.units === IMPERIAL_UNITS);
            }

            // Metric
            const metricCoordinateSystem = this.group.getObjectByName('MetricCoordinateSystem');
            if (metricCoordinateSystem) {
                metricCoordinateSystem.visible = visible && (nextState.units === METRIC_UNITS);
            }

            needUpdateScene = true;
        }

        // Display or hide toolhead
        if (this.toolhead && (this.toolhead.visible !== nextState.objects.toolhead.visible)) {
            this.toolhead.visible = nextState.objects.toolhead.visible;

            needUpdateScene = true;
        }

        // Update work position
        if (!_.isEqual(this.workPosition, nextState.workPosition)) {
            this.workPosition = nextState.workPosition;
            this.setWorkPosition(this.workPosition);

            needUpdateScene = true;
        }

        if (needUpdateScene) {
            this.updateScene({ forceUpdate: forceUpdate });
        }

        if (this.isAgitated !== nextState.isAgitated) {
            this.isAgitated = nextState.isAgitated;

            if (this.isAgitated) {
                // Call renderAnimationLoop when the state changes and isAgitated is true
                requestAnimationFrame(::this.renderAnimationLoop);
            }
        }
    }
    shouldComponentUpdate(nextProps, nextState) {
        if (nextProps.show !== this.props.show) {
            return true;
        }
        return false;
    }
    subscribe() {
        const tokens = [
            pubsub.subscribe('resize', (msg) => {
                this.resizeRenderer();
            })
        ];
        this.pubsubTokens = this.pubsubTokens.concat(tokens);
    }
    unsubscribe() {
        this.pubsubTokens.forEach((token) => {
            pubsub.unsubscribe(token);
        });
        this.pubsubTokens = [];
    }
    getVisibleWidth() {
        const el = ReactDOM.findDOMNode(this.node);

        return (el && el.parentNode && el.parentNode.clientWidth) || 0;
    }
    getVisibleHeight() {
        const el = ReactDOM.findDOMNode(this.node);

        return (el && el.parentNode && el.parentNode.clientHeight) || 0;
    }
    addResizeEventListener() {
        // handle resize event
        if (!(this.onResize)) {
            this.onResize = () => {
                this.resizeRenderer();
            };
        }
        this.onResize();
        this.onResizeThrottled = _.throttle(::this.onResize, 10);
        window.addEventListener('resize', this.onResizeThrottled);
    }
    removeResizeEventListener() {
        // handle resize event
        window.removeEventListener('resize', this.onResizeThrottled);
        this.onResizeThrottled = null;
    }
    resizeRenderer() {
        if (!(this.camera && this.renderer)) {
            return;
        }

        const width = this.getVisibleWidth();
        const height = this.getVisibleHeight();

        if (width === 0 || height === 0) {
            log.warn(`The width (${width}) and height (${height}) cannot be a zero value`);<commit_msg>Ensure the visible width and height are greater than zero<commit_after>/* eslint-disable import/no-unresolved */
import 'imports-loader?THREE=three!three/examples/js/cameras/CombinedCamera';
/* eslint-enable */
import _ from 'lodash';
import colornames from 'colornames';
import pubsub from 'pubsub-js';
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import * as THREE from 'three';
import Detector from 'three/examples/js/Detector';
import log from '../../lib/log';
import './TrackballControls';
import { getBoundingBox, loadTexture } from './helpers';
import Viewport from './Viewport';
import CoordinateAxes from './CoordinateAxes';
import ToolHead from './ToolHead';
import TargetPoint from './TargetPoint';
import GridLine from './GridLine';
import PivotPoint3 from './PivotPoint3';
import TextSprite from './TextSprite';
import GCodeVisualizer from './GCodeVisualizer';
import {
    IMPERIAL_UNITS,
    METRIC_UNITS
} from '../../constants';
import {
    CAMERA_MODE_PAN,
    CAMERA_MODE_ROTATE
} from './constants';

const IMPERIAL_GRID_COUNT = 32; // 32 in
const IMPERIAL_GRID_SPACING = 25.4; // 1 in
const IMPERIAL_AXIS_LENGTH = IMPERIAL_GRID_SPACING * 12; // 12 in
const METRIC_GRID_COUNT = 60; // 60 cm
const METRIC_GRID_SPACING = 10; // 10 mm
const METRIC_AXIS_LENGTH = METRIC_GRID_SPACING * 30; // 300 mm
const CAMERA_VIEWPORT_WIDTH = 300; // 300 mm
const CAMERA_VIEWPORT_HEIGHT = 300; // 300 mm
const PERSPECTIVE_FOV = 70;
const PERSPECTIVE_NEAR = 0.001;
const PERSPECTIVE_FAR = 2000;
const ORTHOGRAPHIC_FOV = 35;
const ORTHOGRAPHIC_NEAR = 0.001;
const ORTHOGRAPHIC_FAR = 2000;
const CAMERA_POSITION_X = 0;
const CAMERA_POSITION_Y = 0;
const CAMERA_POSITION_Z = 200; // Move the camera out a bit from the origin (0, 0, 0)
const TRACKBALL_CONTROLS_MIN_DISTANCE = 1;
const TRACKBALL_CONTROLS_MAX_DISTANCE = 2000;

class Visualizer extends Component {
    static propTypes = {
        show: PropTypes.bool,
        state: PropTypes.object
    };

    pubsubTokens = [];
    isAgitated = false;
    workPosition = {
        x: 0,
        y: 0,
        z: 0
    };
    group = new THREE.Group();
    pivotPoint = new PivotPoint3({ x: 0, y: 0, z: 0 }, (x, y, z) => { // relative position
        _.each(this.group.children, (o) => {
            o.translateX(x);
            o.translateY(y);
            o.translateZ(z);
        });

        // Update the scene
        this.updateScene();
    });

    componentWillMount() {
        // Three.js
        this.renderer = null;
        this.scene = null;
        this.camera = null;
        this.controls = null;
        this.viewport = null;
        this.toolhead = null;
        this.targetPoint = null;
        this.visualizer = null;
    }
    componentDidMount() {
        this.subscribe();
        this.addResizeEventListener();

        if (this.node) {
            const el = ReactDOM.findDOMNode(this.node);
            this.createScene(el);
            this.resizeRenderer();
        }
    }
    componentWillUnmount() {
        this.unsubscribe();
        this.removeResizeEventListener();
        this.clearScene();
    }
    componentWillReceiveProps(nextProps) {
        let forceUpdate = false;
        let needUpdateScene = false;
        const state = this.props.state;
        const nextState = nextProps.state;

        // Enable or disable 3D view
        if ((this.props.show !== nextProps.show) && (!!nextProps.show === true)) {
            this.viewport.update();

            // Set forceUpdate to true when enabling or disabling 3D view
            forceUpdate = true;
            needUpdateScene = true;
        }

        // Update visualizer's frame index
        if (this.visualizer) {
            const frameIndex = nextState.gcode.sent;
            this.visualizer.setFrameIndex(frameIndex);
        }

        // Projection
        if (state.projection !== nextState.projection) {
            if (nextState.projection === 'orthographic') {
                this.camera.toOrthographic();
                this.camera.setZoom(1);
                this.camera.setFov(ORTHOGRAPHIC_FOV);
            } else {
                this.camera.toPerspective();
                this.camera.setZoom(1);
                this.camera.setFov(PERSPECTIVE_FOV);
            }
            if (this.viewport) {
                this.viewport.update();
            }
            needUpdateScene = true;
        }

        // Camera Mode
        if (state.cameraMode !== nextState.cameraMode) {
            this.setCameraMode(nextState.cameraMode);
            needUpdateScene = true;
        }

        // Display the name of the G-code file
        if (state.gcode.displayName !== nextState.gcode.displayName) {
            const gcodeDisplayName = this.group.getObjectByName('GCodeDisplayName');
            if (gcodeDisplayName) {
                gcodeDisplayName.visible = nextState.gcode.displayName;

                needUpdateScene = true;
            }
        }

        // Display or hide coordinate system
        if ((nextState.units !== state.units) ||
            (nextState.objects.coordinateSystem.visible !== state.objects.coordinateSystem.visible)) {
            const visible = nextState.objects.coordinateSystem.visible;

            // Imperial
            const imperialCoordinateSystem = this.group.getObjectByName('ImperialCoordinateSystem');
            if (imperialCoordinateSystem) {
                imperialCoordinateSystem.visible = visible && (nextState.units === IMPERIAL_UNITS);
            }

            // Metric
            const metricCoordinateSystem = this.group.getObjectByName('MetricCoordinateSystem');
            if (metricCoordinateSystem) {
                metricCoordinateSystem.visible = visible && (nextState.units === METRIC_UNITS);
            }

            needUpdateScene = true;
        }

        // Display or hide toolhead
        if (this.toolhead && (this.toolhead.visible !== nextState.objects.toolhead.visible)) {
            this.toolhead.visible = nextState.objects.toolhead.visible;

            needUpdateScene = true;
        }

        // Update work position
        if (!_.isEqual(this.workPosition, nextState.workPosition)) {
            this.workPosition = nextState.workPosition;
            this.setWorkPosition(this.workPosition);

            needUpdateScene = true;
        }

        if (needUpdateScene) {
            this.updateScene({ forceUpdate: forceUpdate });
        }

        if (this.isAgitated !== nextState.isAgitated) {
            this.isAgitated = nextState.isAgitated;

            if (this.isAgitated) {
                // Call renderAnimationLoop when the state changes and isAgitated is true
                requestAnimationFrame(::this.renderAnimationLoop);
            }
        }
    }
    shouldComponentUpdate(nextProps, nextState) {
        if (nextProps.show !== this.props.show) {
            return true;
        }
        return false;
    }
    subscribe() {
        const tokens = [
            pubsub.subscribe('resize', (msg) => {
                this.resizeRenderer();
            })
        ];
        this.pubsubTokens = this.pubsubTokens.concat(tokens);
    }
    unsubscribe() {
        this.pubsubTokens.forEach((token) => {
            pubsub.unsubscribe(token);
        });
        this.pubsubTokens = [];
    }
    getVisibleWidth() {
        const el = ReactDOM.findDOMNode(this.node);
        const sidebarWidth = 60; // TODO

        return (el && el.parentNode && el.parentNode.clientWidth) || (window.innerWidth - sidebarWidth);
    }
    getVisibleHeight() {
        const el = ReactDOM.findDOMNode(this.node);
        const navbarHeight = 50; // TODO

        return (el && el.parentNode && el.parentNode.clientHeight) || (window.innerHeight - navbarHeight);
    }
    addResizeEventListener() {
        // handle resize event
        if (!(this.onResize)) {
            this.onResize = () => {
                this.resizeRenderer();
            };
        }
        this.onResize();
        this.onResizeThrottled = _.throttle(::this.onResize, 10);
        window.addEventListener('resize', this.onResizeThrottled);
    }
    removeResizeEventListener() {
        // handle resize event
        window.removeEventListener('resize', this.onResizeThrottled);
        this.onResizeThrottled = null;
    }
    resizeRenderer() {
        if (!(this.camera && this.renderer)) {
            return;
        }

        const width = this.getVisibleWidth();
        const height = this.getVisibleHeight();

        if (width === 0 || height === 0) {
            log.warn(`The width (${width}) and height (${height}) cannot be a zero value`);
{'fraction_non_alphanumeric': 0.11244088971918968, 'fraction_numerical': 0.01050849436627941, 'mean_word_length': 2.13506588579795, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4217270', 'n_tokens_mistral': 5133, 'n_tokens_neox': 4875, 'n_words': 1054}