<commit_before>import React from 'react';

import THREE from 'three';

import React3 from '../../React3/React3';

import ExampleBase from '../ExampleBase';

import Rect from './Rect';

import Shape from './Shape';

class Geometries extends ExampleBase {
  constructor(props, context) {
    super(props, context);

    this.cameraPosition = new THREE.Vector3(0, 150, 500);
    this.groupPosition = new THREE.Vector3(0, 50, 0);

    this.textureRepeat = new THREE.Vector2(0.008, 0.008);

    const californiaPts = [];

    californiaPts.push(new THREE.Vector2(610, 320));
    californiaPts.push(new THREE.Vector2(450, 300));
    californiaPts.push(new THREE.Vector2(392, 392));
    californiaPts.push(new THREE.Vector2(266, 438));
    californiaPts.push(new THREE.Vector2(190, 570));
    californiaPts.push(new THREE.Vector2(190, 600));
    californiaPts.push(new THREE.Vector2(160, 620));
    californiaPts.push(new THREE.Vector2(160, 650));
    californiaPts.push(new THREE.Vector2(180, 640));
    californiaPts.push(new THREE.Vector2(165, 680));
    californiaPts.push(new THREE.Vector2(150, 670));
    californiaPts.push(new THREE.Vector2(90, 737));
    californiaPts.push(new THREE.Vector2(80, 795));
    californiaPts.push(new THREE.Vector2(50, 835));
    californiaPts.push(new THREE.Vector2(64, 870));
    californiaPts.push(new THREE.Vector2(60, 945));
    californiaPts.push(new THREE.Vector2(300, 945));
    californiaPts.push(new THREE.Vector2(300, 743));
    californiaPts.push(new THREE.Vector2(600, 473));
    californiaPts.push(new THREE.Vector2(626, 425));
    californiaPts.push(new THREE.Vector2(600, 370));
    californiaPts.push(new THREE.Vector2(610, 320));

    for (let i = 0; i < californiaPts.length; i++) californiaPts[i].multiplyScalar(0.25);

    this.californiaPts = californiaPts;
    // this.state = {
    //   ...this.state,
    //   timer: Date.now() * 0.0001,
    // };
  }

  // _onAnimate = () => {
  //   this._onAnimateInternal();
  // };

  _onAnimateInternal() {
    const timer = Date.now() * 0.0001;

    this.setState({
      timer,
    });
  }

  render() {
    const {
      width,
      height,
      // timer,
      } = this.state;

    const x = 0;
    const y = 0;

    const sqLength = 80;

    const rectLength = 120;
    const rectWidth = 40;

    return (

      <React3
        width={width}
        height={height}
        antialias
        pixelRatio={window.devicePixelRatio}
        mainCamera="mainCamera"
        clearColor={0xf0f0f0}
      >
        <scene>
          <perspectiveCamera
            name="mainCamera"
            fov={50}
            aspect={width / height}
            near={1}
            far={1000}

            position={this.cameraPosition}
          >
            <pointLight
              color={0xffffff}
              intensity={0.8}
            />
          </perspectiveCamera>

          <group
            position={this.groupPosition}
          >
            <resources>
              <texture
                resourceId="texture"
                url="textures/UV_Grid_Sm.jpg"
                wrapS={THREE.RepeatWrapping}
                wrapT={THREE.RepeatWrapping}
                repeat={this.textureRepeat}
              />
              <meshPhongMaterial
                resourceId="phongMaterial"
                side={THREE.DoubleSide}
              >
                <textureResource
                  resourceId="texture"
                />
              </meshPhongMaterial>
              <shape
                resourceId="california"
                points={this.californiaPts}
              />
              <shape resourceId="triangle">
                <moveTo
                  x={80}
                  y={20}
                />
                <lineTo
                  x={40}
                  y={80}
                />
                <lineTo
                  x={120}
                  y={80}
                />
                <lineTo
                  x={80}
                  y={20}
                />
              </shape>
              <shape resourceId="heart">
                <moveTo
                  x={x + 25}
                  y={y + 25}
                />
                <bezierCurveTo
                  cp1X={x + 25}
                  cp1Y={y + 25}
                  cp2X={x + 20}
                  cp2Y={y}
                  aX={x}
                  aY={y}
                />
                <bezierCurveTo
                  cp1X={x - 30}
                  cp1Y={y}
                  cp2X={x - 30}
                  cp2Y={y + 35}
                  aX={x - 30}
                  aY={y + 35}
                />
                <bezierCurveTo
                  cp1X={x - 30}
                  cp1Y={y + 55}
                  cp2X={x - 10}
                  cp2Y={y + 77}
                  aX={x + 25}
                  aY={y + 95}
                />
                <bezierCurveTo
                  cp1X={x + 60}
                  cp1Y={y + 77}
                  cp2X={x + 80}
                  cp2Y={y + 55}
                  aX={x + 80}
                  aY={y + 35}
                />
                <bezierCurveTo
                  cp1X={ x + 80}
                  cp1Y={y + 35}
                  cp2X={x + 80}
                  cp2Y={y}
                  aX={x + 50}
                  aY={y}
                />
                <bezierCurveTo
                  cp1X={x + 35}
                  cp1Y={y}
                  cp2X={x + 25}
                  cp2Y={y + 25}
                  aX={x + 25}
                  aY={y + 25}
                />
              </shape>
              <Rect
                resourceId="square"
                width={sqLength}
                length={sqLength}
              />
              <Rect
                resourceId="rect"
                width={rectWidth}
                length={rectLength}
              />
              {(function roundedRect(rectX, rectY,
                                     roundedRectWidth, roundedRectHeight,
                                     radius) {
                return (<shape resourceId="roundedRect">
                  <moveTo
                    x={rectX}
                    y={rectY + radius}
                  />
                  <lineTo
                    x={rectX}
                    y={rectY + roundedRectHeight - radius}
                  />
                  <quadraticCurveTo
                    cpX={rectX}
                    cpY={rectY + roundedRectHeight}
                    x={rectX + radius}
                    y={rectY + roundedRectHeight}
                  />
                  <lineTo
                    x={rectX + roundedRectWidth - radius}
                    y={rectY + roundedRectHeight}
                  />
                  <quadraticCurveTo
                    cpX={rectX + roundedRectWidth}
                    cpY={rectY + roundedRectHeight}
                    x={rectX + roundedRectWidth}
                    y={rectY + roundedRectHeight - radius}
                  />
                  <lineTo
                    x={rectX + roundedRectWidth}
                    y={rectY + radius}
                  />
                  <quadraticCurveTo
                    cpX={rectX + roundedRectWidth}
                    cpY={rectY}
                    x={rectX + roundedRectWidth - radius}
                    y={rectY}
                  />
                  <lineTo
                    x={rectX + radius}
                    y={rectY}
                  />
                  <quadraticCurveTo
                    cpX={rectX}
                    cpY={rectY}
                    x={rectX}
                    y={rectY + radius}
                  />
                </shape>);
              })(0, 0, 50, 50, 20)}
              <shape
                resourceId="track"
              >
                <moveTo
                  x={40}
                  y={40}
                />
                <lineTo
                  x={40}
                  y={160}
                />
                <absArc
                  x={60}
                  y={160}
                  radius={20}
                  startAngle={Math.PI}
                  endAngle={0}
                  clockwise
                />
                <lineTo
                  x={80}
                  y={40}
                />
                <absArc
                  x={60}
                  y={40}
                  radius={20}
                  startAngle={2 * Math.PI}
                  endAngle={Math.PI}
                  clockwise
                />
              </shape>
              {(function circleShape() {
                const circleRadius = 40;

                return (<shape resourceId="circle">
                  <moveTo
                    x={0}
                    y={circleRadius}
                  />
                  <quadraticCurveTo
                    cpX={circleRadius}
                    cpY={circleRadius}
                    x={circleRadius}
                    y={0}
                  />
                  <quadraticCurveTo
                    cpX={circleRadius}
                    cpY={-circleRadius}
                    x={0}
                    y={-circleRadius}
                  />
                  <quadraticCurveTo
                    cpX={-circleRadius}
                    cpY={-circleRadius}
                    x={-circleRadius}
                    y={0}
                  />
                  <quadraticCurveTo
                    cpX={-circleRadius}
                    cpY={circleRadius}
                    x={0}
                    y={circleRadius}
                  />
                </shape>);
              })()}
              <shape resourceId="arc">
                <moveTo
                  x={50}
                  y={10}
                />
                <absArc
                  x={10}
                  y={10}
                  radius={40}
                  startAngle={0}
                  endAngle={Math.PI * 2}
                  clockwise={false}
                />
                <hole>
                  <moveTo
                    x={20}
                    y={10}
                  />
                  <absArc
                    x={10}
                    y={10}
                    radius={10}
                    startAngle={0}
                    endAngle={Math.PI * 2}
                    clockwise
                  />
                </hole>
              </shape>
              <shape resourceId="fish">
                <moveTo
                  x={x}
                  y={y}
                />
                <quadraticCurveTo
                  cpX={x + 50}
                  cpY={y - 80}
                  x={x + 90}
                  y={y - 10}
                />
                <quadraticCurveTo
                  cpX={x + 100}
                  cpY={y - 10}
                  x={x + 115}
                  y={y - 40}
                />
                <quadraticCurveTo
                  cpX={x + 115}
                  cpY={y}
                  x={x + 115}
                  y={y + 40}
                />
                <quadraticCurveTo
                  cpX={x + 100}
                  cpY={y + 10}
                  x={x + 90}
                  y={y + 10}
                />
                <quadraticCurveTo
                  cpX={x + 50}
                  cpY={y + 80}
                  x={x}
                  y={y}
                />
              </shape>
              <shape resourceId="smiley">
                <moveTo
                  x={80}
                  y={40}
                />
                <absArc
                  x={40}
                  y={40}
                  radius={40}
                  startAngle={0}
                  endAngle={Math.PI * 2}
                  clockwise={false}
                />
                <hole key="eye1">
                  <moveTo
                    x={35}
                    y={20}
                  />
                  <absEllipse
                    x={25}
                    y={20}
                    xRadius={10}
                    yRadius={10}
                    startAngle={0}
                    endAngle={Math.PI * 2}
                    clockwise
                  />
                </hole>
                <hole key="eye2">
                  <moveTo
                    x={65}
                    y={20}
                  />
                  <absArc
                    x={55}
                    y={20}
                    radius={10}
                    startAngle={0}
                    endAngle={Math.PI * 2}
                    clockwise
                  />
                </hole>
                <hole key="mouth">
                  <moveTo
                    x={20}
                    y={40}
                  />
                  <quadraticCurveTo
                    cpX={40}
                    cpY={60}
                    x={60}
                    y={40}
                  />
                  <bezierCurveTo
                    cp1X={70}
                    cp1Y={45}
                    cp2X={70}
                    cp2Y={50}
                    aX={60}
                    aY={60}
                  />
                  <quadraticCurveTo
                    cpX={40}
                    cpY={80}
                    x={20}
                    y={60}
                  />
                  <quadraticCurveTo
                    cpX={5}
                    cpY={50}
                    x={20}
                    y={40}
                  />
                </hole>
              </shape>
              {(function splineShape() {
                const splinePoints = [];
                splinePoints.push(new THREE.Vector2(70, 20));
                splinePoints.push(new THREE.Vector2(80, 90));
                splinePoints.push(new THREE.Vector2(-30, 70));
                splinePoints.push(new THREE.Vector2(0, 0));

                return (<shape resourceId="spline">
                  <moveTo
                    x={0}
                    y={0}
                  />
                  <splineThru
                    points={splinePoints}
                  />
                </shape>);
              })()}
            </resources>
            <Shape
              resourceId="california"
              x={-300}
              y={-100}
              z={0}
              color={0xf08000}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="triangle"
              color={0x8080f0}
              x={-180}
              y={0}
              z={0}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="roundedRect"
              x={-150}
              y={150}
              z={0}
              color={0x008000}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="track"
              x={200}
              y={-100}
              z={0}
              color={0x008080}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="square"
              color={0x0040f0}
              x={150}
              y={100}
              z={0}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="heart"
              color={0xf00000}
              x={60}
              y={100}
              z={0}
              rx={0}
              ry={0}
              rz={Math.PI}
              s={1}
            />
            <Shape
              resourceId="circle"
              color={0x00f000}
              x={120}
              y={250}
              z={0}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="fish"
              color={0x404040}
              x={-60}
              y={200}
              z={0}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="smiley"
              x={-200}
              y={250}
              z={0}
              color={0xf000f0}
              rx={0}
              ry={0}
              rz={Math.PI}
              s={1}
            />
            <Shape
              resourceId="arc"
              color={0x804000}
              x={150}
              y={0}
              z={0}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
          </group>

        </scene>
      </React3>

);
  }
}

export default Geometries;
<commit_msg>Minor cleanup<commit_after>import React from 'react';

import THREE from 'three';

import React3 from '../../React3/React3';

import ExampleBase from '../ExampleBase';

import Rect from './Rect';

import Shape from './Shape';

class Geometries extends ExampleBase {
  constructor(props, context) {
    super(props, context);

    this.cameraPosition = new THREE.Vector3(0, 150, 500);
    this.groupPosition = new THREE.Vector3(0, 50, 0);

    this.textureRepeat = new THREE.Vector2(0.008, 0.008);

    const californiaPts = [];

    californiaPts.push(new THREE.Vector2(610, 320));
    californiaPts.push(new THREE.Vector2(450, 300));
    californiaPts.push(new THREE.Vector2(392, 392));
    californiaPts.push(new THREE.Vector2(266, 438));
    californiaPts.push(new THREE.Vector2(190, 570));
    californiaPts.push(new THREE.Vector2(190, 600));
    californiaPts.push(new THREE.Vector2(160, 620));
    californiaPts.push(new THREE.Vector2(160, 650));
    californiaPts.push(new THREE.Vector2(180, 640));
    californiaPts.push(new THREE.Vector2(165, 680));
    californiaPts.push(new THREE.Vector2(150, 670));
    californiaPts.push(new THREE.Vector2(90, 737));
    californiaPts.push(new THREE.Vector2(80, 795));
    californiaPts.push(new THREE.Vector2(50, 835));
    californiaPts.push(new THREE.Vector2(64, 870));
    californiaPts.push(new THREE.Vector2(60, 945));
    californiaPts.push(new THREE.Vector2(300, 945));
    californiaPts.push(new THREE.Vector2(300, 743));
    californiaPts.push(new THREE.Vector2(600, 473));
    californiaPts.push(new THREE.Vector2(626, 425));
    californiaPts.push(new THREE.Vector2(600, 370));
    californiaPts.push(new THREE.Vector2(610, 320));

    for (let i = 0; i < californiaPts.length; i++) californiaPts[i].multiplyScalar(0.25);

    this.californiaPts = californiaPts;
  }

  render() {
    const {
      width,
      height,
      } = this.state;

    const x = 0;
    const y = 0;

    const sqLength = 80;

    const rectLength = 120;
    const rectWidth = 40;

    return (

      <React3
        width={width}
        height={height}
        antialias
        pixelRatio={window.devicePixelRatio}
        mainCamera="mainCamera"
        clearColor={0xf0f0f0}
      >
        <scene>
          <perspectiveCamera
            name="mainCamera"
            fov={50}
            aspect={width / height}
            near={1}
            far={1000}

            position={this.cameraPosition}
          >
            <pointLight
              color={0xffffff}
              intensity={0.8}
            />
          </perspectiveCamera>

          <group
            position={this.groupPosition}
          >
            <resources>
              <texture
                resourceId="texture"
                url="textures/UV_Grid_Sm.jpg"
                wrapS={THREE.RepeatWrapping}
                wrapT={THREE.RepeatWrapping}
                repeat={this.textureRepeat}
              />
              <meshPhongMaterial
                resourceId="phongMaterial"
                side={THREE.DoubleSide}
              >
                <textureResource
                  resourceId="texture"
                />
              </meshPhongMaterial>
              <shape
                resourceId="california"
                points={this.californiaPts}
              />
              <shape resourceId="triangle">
                <moveTo
                  x={80}
                  y={20}
                />
                <lineTo
                  x={40}
                  y={80}
                />
                <lineTo
                  x={120}
                  y={80}
                />
                <lineTo
                  x={80}
                  y={20}
                />
              </shape>
              <shape resourceId="heart">
                <moveTo
                  x={x + 25}
                  y={y + 25}
                />
                <bezierCurveTo
                  cp1X={x + 25}
                  cp1Y={y + 25}
                  cp2X={x + 20}
                  cp2Y={y}
                  aX={x}
                  aY={y}
                />
                <bezierCurveTo
                  cp1X={x - 30}
                  cp1Y={y}
                  cp2X={x - 30}
                  cp2Y={y + 35}
                  aX={x - 30}
                  aY={y + 35}
                />
                <bezierCurveTo
                  cp1X={x - 30}
                  cp1Y={y + 55}
                  cp2X={x - 10}
                  cp2Y={y + 77}
                  aX={x + 25}
                  aY={y + 95}
                />
                <bezierCurveTo
                  cp1X={x + 60}
                  cp1Y={y + 77}
                  cp2X={x + 80}
                  cp2Y={y + 55}
                  aX={x + 80}
                  aY={y + 35}
                />
                <bezierCurveTo
                  cp1X={ x + 80}
                  cp1Y={y + 35}
                  cp2X={x + 80}
                  cp2Y={y}
                  aX={x + 50}
                  aY={y}
                />
                <bezierCurveTo
                  cp1X={x + 35}
                  cp1Y={y}
                  cp2X={x + 25}
                  cp2Y={y + 25}
                  aX={x + 25}
                  aY={y + 25}
                />
              </shape>
              <Rect
                resourceId="square"
                width={sqLength}
                length={sqLength}
              />
              <Rect
                resourceId="rect"
                width={rectWidth}
                length={rectLength}
              />
              {(function roundedRect(rectX, rectY,
                                     roundedRectWidth, roundedRectHeight,
                                     radius) {
                return (<shape resourceId="roundedRect">
                  <moveTo
                    x={rectX}
                    y={rectY + radius}
                  />
                  <lineTo
                    x={rectX}
                    y={rectY + roundedRectHeight - radius}
                  />
                  <quadraticCurveTo
                    cpX={rectX}
                    cpY={rectY + roundedRectHeight}
                    x={rectX + radius}
                    y={rectY + roundedRectHeight}
                  />
                  <lineTo
                    x={rectX + roundedRectWidth - radius}
                    y={rectY + roundedRectHeight}
                  />
                  <quadraticCurveTo
                    cpX={rectX + roundedRectWidth}
                    cpY={rectY + roundedRectHeight}
                    x={rectX + roundedRectWidth}
                    y={rectY + roundedRectHeight - radius}
                  />
                  <lineTo
                    x={rectX + roundedRectWidth}
                    y={rectY + radius}
                  />
                  <quadraticCurveTo
                    cpX={rectX + roundedRectWidth}
                    cpY={rectY}
                    x={rectX + roundedRectWidth - radius}
                    y={rectY}
                  />
                  <lineTo
                    x={rectX + radius}
                    y={rectY}
                  />
                  <quadraticCurveTo
                    cpX={rectX}
                    cpY={rectY}
                    x={rectX}
                    y={rectY + radius}
                  />
                </shape>);
              })(0, 0, 50, 50, 20)}
              <shape
                resourceId="track"
              >
                <moveTo
                  x={40}
                  y={40}
                />
                <lineTo
                  x={40}
                  y={160}
                />
                <absArc
                  x={60}
                  y={160}
                  radius={20}
                  startAngle={Math.PI}
                  endAngle={0}
                  clockwise
                />
                <lineTo
                  x={80}
                  y={40}
                />
                <absArc
                  x={60}
                  y={40}
                  radius={20}
                  startAngle={2 * Math.PI}
                  endAngle={Math.PI}
                  clockwise
                />
              </shape>
              {(function circleShape() {
                const circleRadius = 40;

                return (<shape resourceId="circle">
                  <moveTo
                    x={0}
                    y={circleRadius}
                  />
                  <quadraticCurveTo
                    cpX={circleRadius}
                    cpY={circleRadius}
                    x={circleRadius}
                    y={0}
                  />
                  <quadraticCurveTo
                    cpX={circleRadius}
                    cpY={-circleRadius}
                    x={0}
                    y={-circleRadius}
                  />
                  <quadraticCurveTo
                    cpX={-circleRadius}
                    cpY={-circleRadius}
                    x={-circleRadius}
                    y={0}
                  />
                  <quadraticCurveTo
                    cpX={-circleRadius}
                    cpY={circleRadius}
                    x={0}
                    y={circleRadius}
                  />
                </shape>);
              })()}
              <shape resourceId="arc">
                <moveTo
                  x={50}
                  y={10}
                />
                <absArc
                  x={10}
                  y={10}
                  radius={40}
                  startAngle={0}
                  endAngle={Math.PI * 2}
                  clockwise={false}
                />
                <hole>
                  <moveTo
                    x={20}
                    y={10}
                  />
                  <absArc
                    x={10}
                    y={10}
                    radius={10}
                    startAngle={0}
                    endAngle={Math.PI * 2}
                    clockwise
                  />
                </hole>
              </shape>
              <shape resourceId="fish">
                <moveTo
                  x={x}
                  y={y}
                />
                <quadraticCurveTo
                  cpX={x + 50}
                  cpY={y - 80}
                  x={x + 90}
                  y={y - 10}
                />
                <quadraticCurveTo
                  cpX={x + 100}
                  cpY={y - 10}
                  x={x + 115}
                  y={y - 40}
                />
                <quadraticCurveTo
                  cpX={x + 115}
                  cpY={y}
                  x={x + 115}
                  y={y + 40}
                />
                <quadraticCurveTo
                  cpX={x + 100}
                  cpY={y + 10}
                  x={x + 90}
                  y={y + 10}
                />
                <quadraticCurveTo
                  cpX={x + 50}
                  cpY={y + 80}
                  x={x}
                  y={y}
                />
              </shape>
              <shape resourceId="smiley">
                <moveTo
                  x={80}
                  y={40}
                />
                <absArc
                  x={40}
                  y={40}
                  radius={40}
                  startAngle={0}
                  endAngle={Math.PI * 2}
                  clockwise={false}
                />
                <hole key="eye1">
                  <moveTo
                    x={35}
                    y={20}
                  />
                  <absEllipse
                    x={25}
                    y={20}
                    xRadius={10}
                    yRadius={10}
                    startAngle={0}
                    endAngle={Math.PI * 2}
                    clockwise
                  />
                </hole>
                <hole key="eye2">
                  <moveTo
                    x={65}
                    y={20}
                  />
                  <absArc
                    x={55}
                    y={20}
                    radius={10}
                    startAngle={0}
                    endAngle={Math.PI * 2}
                    clockwise
                  />
                </hole>
                <hole key="mouth">
                  <moveTo
                    x={20}
                    y={40}
                  />
                  <quadraticCurveTo
                    cpX={40}
                    cpY={60}
                    x={60}
                    y={40}
                  />
                  <bezierCurveTo
                    cp1X={70}
                    cp1Y={45}
                    cp2X={70}
                    cp2Y={50}
                    aX={60}
                    aY={60}
                  />
                  <quadraticCurveTo
                    cpX={40}
                    cpY={80}
                    x={20}
                    y={60}
                  />
                  <quadraticCurveTo
                    cpX={5}
                    cpY={50}
                    x={20}
                    y={40}
                  />
                </hole>
              </shape>
              {(function splineShape() {
                const splinePoints = [];
                splinePoints.push(new THREE.Vector2(70, 20));
                splinePoints.push(new THREE.Vector2(80, 90));
                splinePoints.push(new THREE.Vector2(-30, 70));
                splinePoints.push(new THREE.Vector2(0, 0));

                return (<shape resourceId="spline">
                  <moveTo
                    x={0}
                    y={0}
                  />
                  <splineThru
                    points={splinePoints}
                  />
                </shape>);
              })()}
            </resources>
            <Shape
              resourceId="california"
              x={-300}
              y={-100}
              z={0}
              color={0xf08000}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="triangle"
              color={0x8080f0}
              x={-180}
              y={0}
              z={0}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="roundedRect"
              x={-150}
              y={150}
              z={0}
              color={0x008000}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="track"
              x={200}
              y={-100}
              z={0}
              color={0x008080}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="square"
              color={0x0040f0}
              x={150}
              y={100}
              z={0}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="heart"
              color={0xf00000}
              x={60}
              y={100}
              z={0}
              rx={0}
              ry={0}
              rz={Math.PI}
              s={1}
            />
            <Shape
              resourceId="circle"
              color={0x00f000}
              x={120}
              y={250}
              z={0}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="fish"
              color={0x404040}
              x={-60}
              y={200}
              z={0}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
            <Shape
              resourceId="smiley"
              x={-200}
              y={250}
              z={0}
              color={0xf000f0}
              rx={0}
              ry={0}
              rz={Math.PI}
              s={1}
            />
            <Shape
              resourceId="arc"
              color={0x804000}
              x={150}
              y={0}
              z={0}
              rx={0}
              ry={0}
              rz={0}
              s={1}
            />
          </group>

        </scene>
      </React3>

);
  }
}

export default Geometries;
<|endoftext|>


<commit_before>const vec3 = require('gl-vec3')
const mat4 = require('gl-mat4')

/*
const v3 = vec3.create()
const m4 = mat4.create()*/

export const params = {
  enabled: true,
  userZoom: true,
  userZoomSpeed: 1.0,
  userRotate: true,
  userRotateSpeed: 1.0,

  userPan: true,
  userPanSpeed: 2.0,

  autoRotate: false,
  autoRotateSpeed: 2.0, // 30 seconds per round when fps is 60

  minPolarAngle: 0, // radians
  maxPolarAngle: Math.PI, // radians

  minDistance: 0.2,
  maxDistance: 1400,

  active: false,
  mainPointerPressed: false,

  EPS: 0.000001,
  PIXELS_PER_ROUND: 1800,

  up: [0, 1, 0],

  // ///not sure about theses
  camStates: [
    {
      phiDelta: 0,
      thetaDelta: 0,
      scale: 1,
      lastPosition: [0, 0, 0]
    }
  ],

  centers: [
    [0, 0, 0]
  ],
  objects: [
    {position: [0, 0, 0]}
  ]
}

export function update (params, dt) {
  const {EPS, up} = params
  // this is a modified version, with inverted Y and Z (since we use camera[2] => up)
  // we also allow multiple objects/cameras
  for (var i = 0; i < params.objects.length;i++) {
    var object = params.objects[i]
    var center = params.centers[i]
    var camState = params.camStates[i]

    let curThetaDelta = camState.thetaDelta
    let curPhiDelta = camState.phiDelta
    let curScale = camState.scale
    let lastPosition = camState.lastPosition

    const position = object.position
    // var offset = position.clone().sub(center)
    const offset = vec3.subtract(vec3.create(), position, center)

    let theta
    let phi

    if (up[2] === 1) {
      // angle from z-axis around y-axis, upVector : z
      theta = Math.atan2(offset[0], offset[1])
      // angle from y-axis
      phi = Math.atan2(Math.sqrt(offset[0] * offset[0] + offset[1] * offset[1]), offset[2])
    } else {
      // in case of y up
      theta = Math.atan2(offset[0], offset[2])
      phi = Math.atan2(Math.sqrt(offset[0] * offset[0] + offset[2] * offset[2]), offset[1])
      curThetaDelta = -(curThetaDelta)
    }

    /*if ( params.autoRotate ) {
      //PER camera
      params.objects.map(function(object, index){
        if(scope.objectOptions[index].userRotate){
          scope.camStates[index].thetaDelta += getAutoRotationAngle()
        }
      })
    }*/

    theta += curThetaDelta
    phi += curPhiDelta

    // restrict phi to be between desired limits
    phi = Math.max(params.minPolarAngle, Math.min(params.maxPolarAngle, phi))
    // restrict phi to be betwee EPS and PI-EPS
    phi = Math.max(EPS, Math.min(Math.PI - EPS, phi))
    // multiply by scaling effect
    let radius = vec3.length(offset) * curScale
    // restrict radius to be between desired limits
    radius = Math.max(params.minDistance, Math.min(params.maxDistance, radius))

    if (up[2] === 1) {
      offset[0] = radius * Math.sin(phi) * Math.sin(theta)
      offset[2] = radius * Math.cos(phi)
      offset[1] = radius * Math.sin(phi) * Math.cos(theta)
    } else {
      offset[0] = radius * Math.sin(phi) * Math.sin(theta)
      offset[1] = radius * Math.cos(phi)
      offset[2] = radius * Math.sin(phi) * Math.cos(theta)
    }

    // position.copy(center).add(offset)
    // object.lookAt(center)

    // temporary
    const eye = vec3.create([10, 0, 10])
    vec3.add(vec3.create(), center, offset)
    const objMat = mat4.lookAt(mat4.create(), eye, center, up)

    const positionChanged = vec3.distance(lastPosition, position) > 0 // TODO optimise
    const results = {
      camState: {
        thetaDelta: curThetaDelta / 1.5,
        phiDelta: curPhiDelta / 1.5,
        scale: 1,
        lastPosition: position
      },
      changed: positionChanged,
      object: {
        mat: objMat
      }
    }
    console.log('results', results)
  /*camState.thetaDelta /= 1.5
  camState.phiDelta /= 1.5
  camState.scale = 1*/
  }
}

/*
function setObservables (observables) {
  let {dragMoves$, zooms$} = observables

  let self = this

  // are these useful ?
  //scope.userZoomSpeed = 0.6
  // onPinch
  function zoom (zoomDir, zoomScale, cameras) {
    if (scope.enabled === false) return
    if (scope.userZoom === false) return

    // PER camera
    cameras.map(function (object, index) {
      if (scope.objectOptions[index].userZoom) {
        if (zoomDir < 0) scope.camStates[index].scale /= zoomScale
        if (zoomDir > 0) scope.camStates[index].scale *= zoomScale
      }
    })
  }

  function rotate (cameras, angle) {
    if (scope.enabled === false) return
    if (scope.userRotate === false) return

    // PER camera
    cameras.map(function (object, index) {
      if (scope.objectOptions[index].userRotate) {
        scope.camStates[index].thetaDelta += angle[0]
        scope.camStates[index].phiDelta += angle[1]
      }
    })
  }

  // TODO: implement
  function pan (cameras, offset) {
    // console.log(event)
    var _origDist = distance.clone()

    // do this PER camera
    cameras.map(function (object, index) {
      if (scope.objectOptions[index].userPan) {
        let distance = _origDist.clone()
        distance.transformDirection(object.matrix)
        distance.multiplyScalar(scope.userPanSpeed)
        object.position.add(distance)
        scope.centers[index].add(distance)
      }
    })
  }

  dragMoves$
    .subscribe(function (e) {
      let delta = e.delta
      let angle = {x: 0,y: 0}
      angle[0] = 2 * Math.PI * delta[0] / PIXELS_PER_ROUND * scope.userRotateSpeed
      angle[1] = -2 * Math.PI * delta[1] / PIXELS_PER_ROUND * scope.userRotateSpeed
      rotate(self.objects, angle)
    })

  zooms$
    .subscribe(function (delta) {
      let zoomScale = undefined
      if (!zoomScale) {
        zoomScale = getZoomScale()
      }
      zoom(delta, zoomScale, self.objects)
    })
}*/
<commit_msg>refactor(orbitControls): a lot more cleanup & simplification of controls<commit_after>const vec3 = require('gl-vec3')
const mat4 = require('gl-mat4')

const {max, min, sqrt, PI, sin, cos, atan2} = Math
/*
const v3 = vec3.create()
const m4 = mat4.create()*/

/* cameras are assumed to have:
 projection
 view
 target (focal point)
 eye/position
 up
*/

export const params = {
  enabled: true,
  userZoom: true,
  userZoomSpeed: 1.0,
  userRotate: true,
  userRotateSpeed: 1.0,

  userPan: true,
  userPanSpeed: 2.0,

  autoRotate: false,
  autoRotateSpeed: 2.0, // 30 seconds per round when fps is 60

  minPolarAngle: 0, // radians
  maxPolarAngle: PI, // radians

  minDistance: 0.2,
  maxDistance: 1400,

  active: false,
  mainPointerPressed: false,

  EPS: 0.000001,
  PIXELS_PER_ROUND: 1800,
  drag: 0.01,// Decrease the momentum by 1% each iteration

  // below this, dynamic stuff mostly
  up: [0, 1, 0],

  // not sure about these
  thetaDelta: 0,
  phiDelta: 0,
  scale: 1,

  position: [0, 0, 0],
  target: [0, 0, 0],
  view: new Float32Array(16)// default, this is just a 4x4 matrix

}

export function update (params) {
  // this is a modified version, with inverted Y and Z (since we use camera[2] => up)
  const {EPS, up, position, target, view} = params
  let curThetaDelta = params.thetaDelta
  let curPhiDelta = params.phiDelta
  let curScale = params.scale

  const offset = vec3.subtract(vec3.create(), position, target)
  let theta
  let phi

  if (up[2] === 1) {
    // angle from z-axis around y-axis, upVector : z
    theta = atan2(offset[0], offset[1])
    // angle from y-axis
    phi = atan2(sqrt(offset[0] * offset[0] + offset[1] * offset[1]), offset[2])
  } else {
    // in case of y up
    theta = atan2(offset[0], offset[2])
    phi = atan2(sqrt(offset[0] * offset[0] + offset[2] * offset[2]), offset[1])
    curThetaDelta = -(curThetaDelta)
  }

  /*if (params.autoRotate && params.userRotate) {
    scope.camStates[index].thetaDelta += getAutoRotationAngle()
  }*/
  theta += curThetaDelta
  phi += curPhiDelta

  // restrict phi to be between desired limits
  phi = max(params.minPolarAngle, min(params.maxPolarAngle, phi))
  // restrict phi to be betwee EPS and PI-EPS
  phi = max(EPS, min(PI - EPS, phi))
  // multiply by scaling effect and restrict radius to be between desired limits
  const radius = max(params.minDistance, min(params.maxDistance, vec3.length(offset) * curScale))

  if (up[2] === 1) {
    offset[0] = radius * sin(phi) * sin(theta)
    offset[2] = radius * cos(phi)
    offset[1] = radius * sin(phi) * cos(theta)
  } else {
    offset[0] = radius * sin(phi) * sin(theta)
    offset[1] = radius * cos(phi)
    offset[2] = radius * sin(phi) * cos(theta)
  }

  // position.copy(target).add(offset)
  // object.lookAt(target)
  const newPosition = vec3.add(vec3.create(), target, offset)
  const newTarget = target
  const newView = mat4.lookAt(view, newPosition, target, up)
  /* mat3.fromMat4(camMat, camMat)
  quat.fromMat3(this.rotation, camMat)
  lookAt(view, this.position, this.target, this.up)
  */

  const positionChanged = vec3.distance(position, newPosition) > 0 // TODO optimise
  const results = {
    changed: positionChanged,

    thetaDelta: curThetaDelta / 1.5,
    phiDelta: curPhiDelta / 1.5,
    scale: curScale,

    position: newPosition,
    target: newTarget,
    view: newView
  }
  console.log('results', results)
  return results
}

export function rotate (params, angle) {
  params.thetaDelta += angle
  params.phiDelta += angle
  return params
}

export function zoom (params, zoomDir, zoomScale) {
  const scale = zoomDir < 0 ? params.scale / zoomScale : params.scale * zoomScale
  params.scale = scale
  return params
}

function pan (params) {

}

/*
function setObservables (observables) {
  let {dragMoves$, zooms$} = observables

  let self = this

  // are these useful ?
  //scope.userZoomSpeed = 0.6
  // onPinch
  function zoom (zoomDir, zoomScale, cameras) {
    if (scope.enabled === false) return
    if (scope.userZoom === false) return

    // PER camera
    cameras.map(function (object, index) {
      if (scope.objectOptions[index].userZoom) {
        if (zoomDir < 0) scope.camStates[index].scale /= zoomScale
        if (zoomDir > 0) scope.camStates[index].scale *= zoomScale
      }
    })
  }

  function rotate (cameras, angle) {
    if (scope.enabled === false) return
    if (scope.userRotate === false) return

    // PER camera
    cameras.map(function (object, index) {
      if (scope.objectOptions[index].userRotate) {
        scope.camStates[index].thetaDelta += angle[0]
        scope.camStates[index].phiDelta += angle[1]
      }
    })
  }

  // TODO: implement
  function pan (cameras, offset) {
    // console.log(event)
    var _origDist = distance.clone()

    // do this PER camera
    cameras.map(function (object, index) {
      if (scope.objectOptions[index].userPan) {
        let distance = _origDist.clone()
        distance.transformDirection(object.matrix)
        distance.multiplyScalar(scope.userPanSpeed)
        object.position.add(distance)
        scope.centers[index].add(distance)
      }
    })
  }

  dragMoves$
    .subscribe(function (e) {
      let delta = e.delta
      let angle = {x: 0,y: 0}
      angle[0] = 2 * Math.PI * delta[0] / PIXELS_PER_ROUND * scope.userRotateSpeed
      angle[1] = -2 * Math.PI * delta[1] / PIXELS_PER_ROUND * scope.userRotateSpeed
      rotate(self.objects, angle)
    })

  zooms$
    .subscribe(function (delta) {
      let zoomScale = undefined
      if (!zoomScale) {
        zoomScale = getZoomScale()
      }
      zoom(delta, zoomScale, self.objects)
    })
}*/
<|endoftext|>


<commit_before>/*
 * @copyright 2011 - present by OpenGamma Inc
 * @license See distribution for license
 */
$.register_module({
    name: 'og.common.gadgets.Gadgets_container',
    dependencies: ['og.common.gadgets.manager'],
    obj: function () {
        var tabs_tmpl, hdr = ' .ui-layout-header', panels = {
            '.OG-layout-analytics-south': 'south',
            '.OG-layout-analytics-dock-north': 'dock-north',
            '.OG-layout-analytics-dock-center': 'dock-center',
            '.OG-layout-analytics-dock-south': 'dock-south'
        };
        return function (container, gadgets_arr) {
            var selector, pane, initialized = false, live_id, counter = 1,
                layout = og.views.common.layout.inner, gadgets = [], gadgets_container = this;
            /**
             * @param {Number} id Active tab id
             */
            var update_tabs = function (id) {
                var template = Handlebars.compile(tabs_tmpl), $header = $(selector + hdr), tabs_arr;
                if (!id) $header.html(template({'tabs': [{'name': 'empty'}]})); // empty tabs
                else {
                    tabs_arr = gadgets.reduce(function (acc, val) {
                        return acc.push({
                            'name': val.name, 'active': (id === val.id), 'delete': true, 'id': val.id
                        }) && acc;
                    }, []);
                    $header.html(template({'tabs': tabs_arr}));
                    show_gadget(id);
                }
            };
            /**
             * @param id Id of gadget to show, hide all others
             */
            var show_gadget = function (id) {
                $(selector).find('.OG-gadget-container [class*=OG-gadget-]').hide().filter('.OG-gadget-' + id).show();
                live_id = id;
            };
            /**
             * @param {String} pane Pane to toggle
             * @param {Boolean} open_only Don't close, only open
             */
            var toggle_pane = function (pane, open_only) {
                var max = '50%', min = $('.OG-layout-analytics-' + pane + ' .OG-gadget-tabs').height();
                layout.state[pane].size === min
                        ? layout.sizePane(pane, max)
                        : open_only ? null : layout.sizePane(pane, min);
            };
            gadgets_container.init = function (container, gadgets_arr) {
                $.when(og.api.text({module: 'og.analytics.tabs_tash'})).then(function (tmpl) {
                    selector = container;
                    pane = panels[selector];
                    tabs_tmpl = tmpl;
                    initialized = true;
                    // setup click handlers
                    $(selector + hdr)
                        .on('click', 'li', function () {
                            if (!$(this).hasClass('og-active')) {
                                var id = +$(this).attr('class').replace(/og\-/, '');
                                update_tabs(id);
                                gadgets[id-1].gadget.resize();
                                toggle_pane(pane, true);
                            }
                        })
                        .on('click', '.og-minimize', function () {toggle_pane(pane);});
                    if (gadgets_arr === void 0) update_tabs();
                    else if ($.isArray(gadgets_arr)) gadgets_container.add(gadgets_arr);
                    else if (gadgets_arr && !$.isArray(gadgets_arr))
                        throw new TypeError('gadgets_arr should be an Array');
                    og.common.gadgets.manager.register(gadgets_container);
                });
            };
            /**
             * @param {Array}          config An array of gadget configuration objects
             * @param {Function}       config[x].gadget
             * @param {Object}         config[x].options
             * @param {String}         config[x].name Tab name
             * @param {Boolean}        config[x].margin Add margin to the container
             */
            gadgets_container.add = function add(config) {
                if (!initialized) return setTimeout(add.partial(config), 10);
                if (!selector) throw new TypeError('Gadgets_container has not been initialized');
                var panel_container = selector + ' .OG-gadget-container';
                config.map(function (obj) {
                    var gadget_class = 'OG-gadget-' + (obj.id = counter++),
                        gadget_selector = panel_container + ' .' + gadget_class;
                    $(panel_container)
                        .append('
')
                        .find('.' + gadget_class)
                        .css({margin: obj.margin ? 10 : 0}); // add margin if requested
                    obj.gadget = new obj.gadget($.extend(true, obj.options, {selector: gadget_selector}));
                    gadgets.push(obj);
                });
                update_tabs(config[0].id);
            };
            gadgets_container.alive = function () {return !!$(selector).length};
            gadgets_container.resize = function () {
                gadgets.forEach(function (obj) {
                    if (obj.id === live_id) $(selector + ' .ui-layout-content').show(), obj.gadget.resize();
                });
            }
        };
    }
});<commit_msg>adding docs<commit_after>/*
 * @copyright 2011 - present by OpenGamma Inc
 * @license See distribution for license
 */
$.register_module({
    name: 'og.common.gadgets.Gadgets_container',
    dependencies: ['og.common.gadgets.manager'],
    obj: function () {
        var tabs_tmpl, hdr = ' .ui-layout-header', panels = {
            '.OG-layout-analytics-south': 'south',
            '.OG-layout-analytics-dock-north': 'dock-north',
            '.OG-layout-analytics-dock-center': 'dock-center',
            '.OG-layout-analytics-dock-south': 'dock-south'
        };
        /**
         * @param {String} container Selector to initialize a GadgetsContainer in
         * @param {Array} gadgets_arr Optional array of gadgets to initialize a GadgetsContainer with
         */
        return function (container, gadgets_arr) {
            var selector, pane, initialized = false, live_id, counter = 1,
                layout = og.views.common.layout.inner, gadgets = [], gadgets_container = this;
            /**
             * @param {Number} id Active tab id
             */
            var update_tabs = function (id) {
                var template = Handlebars.compile(tabs_tmpl), $header = $(selector + hdr), tabs_arr;
                if (!id) $header.html(template({'tabs': [{'name': 'empty'}]})); // empty tabs
                else {
                    tabs_arr = gadgets.reduce(function (acc, val) {
                        return acc.push({
                            'name': val.name, 'active': (id === val.id), 'delete': true, 'id': val.id
                        }) && acc;
                    }, []);
                    $header.html(template({'tabs': tabs_arr}));
                    show_gadget(id);
                }
            };
            /**
             * @param id Id of gadget to show, hide all others
             */
            var show_gadget = function (id) {
                $(selector).find('.OG-gadget-container [class*=OG-gadget-]').hide().filter('.OG-gadget-' + id).show();
                live_id = id;
            };
            /**
             * @param {String} pane Pane to toggle
             * @param {Boolean} open_only Don't close, only open
             */
            var toggle_pane = function (pane, open_only) {
                var max = '50%', min = $('.OG-layout-analytics-' + pane + ' .OG-gadget-tabs').height();
                layout.state[pane].size === min
                        ? layout.sizePane(pane, max)
                        : open_only ? null : layout.sizePane(pane, min);
            };
            gadgets_container.init = function (container, gadgets_arr) {
                $.when(og.api.text({module: 'og.analytics.tabs_tash'})).then(function (tmpl) {
                    selector = container;
                    pane = panels[selector];
                    tabs_tmpl = tmpl;
                    initialized = true;
                    // setup click handlers
                    $(selector + hdr)
                        .on('click', 'li', function () {
                            if (!$(this).hasClass('og-active')) {
                                var id = +$(this).attr('class').replace(/og\-/, '');
                                update_tabs(id);
                                gadgets[id-1].gadget.resize();
                                toggle_pane(pane, true);
                            }
                        })
                        .on('click', '.og-minimize', function () {toggle_pane(pane);});
                    if (gadgets_arr === void 0) update_tabs();
                    else if ($.isArray(gadgets_arr)) gadgets_container.add(gadgets_arr);
                    else if (gadgets_arr && !$.isArray(gadgets_arr))
                        throw new TypeError('gadgets_arr should be an Array');
                    og.common.gadgets.manager.register(gadgets_container);
                });
            };
            /**
             * @param {Array}          config An array of gadget configuration objects
             * @param {Function}       config[x].gadget
             * @param {Object}         config[x].options
             * @param {String}         config[x].name Tab name
             * @param {Boolean}        config[x].margin Add margin to the container
             */
            gadgets_container.add = function add(config) {
                if (!initialized) return setTimeout(add.partial(config), 10);
                if (!selector) throw new TypeError('Gadgets_container has not been initialized');
                var panel_container = selector + ' .OG-gadget-container';
                config.map(function (obj) {
                    var gadget_class = 'OG-gadget-' + (obj.id = counter++),
                        gadget_selector = panel_container + ' .' + gadget_class;
                    $(panel_container)
                        .append('
')
                        .find('.' + gadget_class)
                        .css({margin: obj.margin ? 10 : 0}); // add margin if requested
                    obj.gadget = new obj.gadget($.extend(true, obj.options, {selector: gadget_selector}));
                    gadgets.push(obj);
                });
                update_tabs(config[0].id);
            };
            gadgets_container.alive = function () {return !!$(selector).length};
            gadgets_container.resize = function () {
                gadgets.forEach(function (obj) {
                    if (obj.id === live_id) $(selector + ' .ui-layout-content').show(), obj.gadget.resize();
                });
            }
        };
    }
});<|endoftext|>


<commit_before>const ArgumentType = require('../../extension-support/argument-type');
const BlockType = require('../../extension-support/block-type');
const Cast = require('../../util/cast');
const log = require('../../util/log');
const Base64Util = require('../../util/base64-util');
const BTSession = require('../../io/BTSession');

/**
 * High-level primitives / constants used by the extension.
 * @type {object}
 */
const BTCommand = {
    LAYER: 0x00,
    NUM8: 0x81,
    NUM16: 0x82,
    NUM32: 0x83,
    COAST: 0x0,
    BRAKE: 0x1,
    LONGRAMP: 50,
    STEPSPEED: 0xAE,
    TIMESPEED: 0xAF,
    OUTPUTSTOP: 0xA3,
    OUTPUTRESET: 0xA2,
    STEPSPEEDSYNC: 0xB0,
    TIMESPEEDSYNC: 0xB1
};

/**
 * Array of accepted motor ports.
 * @note These should not be translated as they correspond to labels on
 *       the EV3 hub.
 * @type {array}
 */
const MOTOR_PORTS = [
    {
        name: 'A',
        value: 1
    },
    {
        name: 'B',
        value: 2
    },
    {
        name: 'C',
        value: 4
    },
    {
        name: 'D',
        value: 8
    }
];

/**
 * Array of accepted sensor ports.
 * @note These should not be translated as they correspond to labels on
 *       the EV3 hub.
 * @type {array}
 */
// const SENSOR_PORTS = ['1', '2', '3', '4'];

class EV3 {

    constructor (runtime, extensionId) {

        /**
         * The Scratch 3.0 runtime used to trigger the green flag button.
         * @type {Runtime}
         * @private
         */
        this._runtime = runtime;

        this.connected = false;
        this.speed = 50;

        /**
         * The Bluetooth connection session for reading/writing device data.
         * @type {BTSession}
         * @private
         */
        this._bt = null;
        this._runtime.registerExtensionDevice(extensionId, this);

        // TODO: auto-connect temporary - until button is added
        this.startDeviceScan();
    }

    // TODO: keep here?
    /**
     * Called by the runtime when user wants to scan for a device.
     */
    startDeviceScan () {
        log.info('making a new BT session');
        this._bt = new BTSession(this._runtime, {
            majorDeviceClass: 8,
            minorDeviceClass: 1
        }, this._onSessionConnect.bind(this));
    }

    // TODO: keep here?
    /**
     * Called by the runtime when user wants to connect to a certain device.
     * @param {number} id - the id of the device to connect to.
     */
    connectDevice (id) {
        this._bt.connectDevice(id);
    }

    beep () {
        if (!this.connected) return;
        this._bt.sendMessage({
            message: 'DwAAAIAAAJQBgQKC6AOC6AM=',
            encoding: 'base64'
        });
    }

    motorTurnClockwise (port, time) {
        if (!this.connected) return;

        // Build up motor command
        const cmd = this._applyPrefix(0, this._motorCommand(
            BTCommand.TIMESPEED,
            port,
            time,
            this.speed,
            BTCommand.LONGRAMP
        ));

        // Send message
        this._bt.sendMessage({
            message: Base64Util.arrayBufferToBase64(cmd),
            encoding: 'base64'
        });

        // Yield for time
        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, time);
        });
    }

    motorTurnCounterClockwise (port, time) {
        if (!this.connected) return;

        // Build up motor command
        const cmd = this._applyPrefix(0, this._motorCommand(
            BTCommand.TIMESPEED,
            port,
            time,
            this.speed * -1,
            BTCommand.LONGRAMP
        ));

        // Send message
        this._bt.sendMessage({
            message: Base64Util.arrayBufferToBase64(cmd),
            encoding: 'base64'
        });

        // Yield for time
        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, time);
        });
    }

    _applyPrefix (n, cmd) {
        const len = cmd.length + 5;
        return [].concat(
            len & 0xFF,
            (len >> 8) & 0xFF,
            0x1,
            0x0,
            0x0,
            n,
            0x0,
            cmd
        );
    }

    /**
     * Generate a motor command in EV3 byte array format (CMD, LAYER, PORT,
     * SPEED, RAMP UP, RUN, RAMP DOWN, BREAKING TYPE)
     * @param  {string} command Motor command primitive (i.e. "prefix")
     * @param  {string} port    Port to address
     * @param  {number} n       Value to be passed to motor command
     * @param  {number} speed   Speed value
     * @param  {number} ramp    Ramp value
     * @return {array}          Byte array
     */
    _motorCommand (command, port, n, speed, ramp) {
        /**
         * Generate run values for a given input.
         * @param  {number} run Run input
         * @return {array}      Run values (byte array)
         */
        const getRunValues = function (run) {
            // If run duration is less than max 16-bit integer
            if (run < 0x7fff) {
                return [
                    BTCommand.NUM16,
                    run & 0xff,
                    (run >> 8) & 0xff
                ];
            }

            // Run forever
            return [
                BTCommand.NUM32,
                run & 0xff,
                (run >> 8) & 0xff,
                (run >> 16) & 0xff,
                (run >> 24) & 0xff
            ];
        };

        // If speed is less than zero, make it positive and multiply the input
        // value by -1
        if (speed < 0) {
            speed = -1 * speed;
            n = -1 * n;
        }

        // If the input value is less than 0
        const dir = (n < 0) ? 0x100 - speed : speed; // step negative or possitive
        n = Math.abs(n);

        // Setup motor run duration and ramping behavior
        let rampup = ramp;
        let rampdown = ramp;
        let run = n - ramp * 2;
        if (run < 0) {
            rampup = Math.floor(n / 2);
            run = 0;
            rampdown = n - rampup;
        }

        // Generate motor command
        const runcmd = getRunValues(run);
        return [
            command,
            BTCommand.LAYER,
            port,
            BTCommand.NUM8,
            dir & 0xff,
            BTCommand.NUM8,
            rampup
        ].concat(runcmd.concat([
            BTCommand.NUM8,
            rampdown,
            BTCommand.BRAKE
        ]));
    }

    _onSessionConnect () {
        log.info('bt device connected!');
        this.connected = true;
        // start reading data?
    }

}

class Scratch3Ev3Blocks {

    /**
     * The ID of the extension.
     * @return {string} the id
     */
    static get EXTENSION_ID () {
        return 'ev3';
    }

    /**
     * Creates a new instance of the EV3 extension.
     * @param  {object} runtime VM runtime
     * @constructor
     */
    constructor (runtime) {
        /**
         * The Scratch 3.0 runtime.
         * @type {Runtime}
         */
        this.runtime = runtime;

        // Create a new MicroBit device instance
        this._device = new EV3(this.runtime, Scratch3Ev3Blocks.EXTENSION_ID);
    }

    /**
     * Define the EV3 extension.
     * @return {object} Extension description.
     */
    getInfo () {
        return {
            id: Scratch3Ev3Blocks.EXTENSION_ID,
            name: 'LEGO MINDSTORMS EV3',
            iconURI: null,
            blocks: [
                {
                    opcode: 'motorTurnClockwise',
                    text: '[PORT] turn clockwise [TIME] seconds',
                    blockType: BlockType.COMMAND,
                    arguments: {
                        PORT: {
                            type: ArgumentType.STRING,
                            menu: 'motorPorts',
                            defaultValue: MOTOR_PORTS[0].value
                        },
                        TIME: {
                            type: ArgumentType.NUMBER,
                            defaultValue: 1
                        }
                    }
                },
                {
                    opcode: 'motorTurnCounterClockwise',
                    text: '[PORT] turn counter [TIME] seconds',
                    blockType: BlockType.COMMAND,
                    arguments: {
                        PORT: {
                            type: ArgumentType.STRING,
                            menu: 'motorPorts',
                            defaultValue: MOTOR_PORTS[0].value
                        },
                        TIME: {
                            type: ArgumentType.NUMBER,
                            defaultValue: 1
                        }
                    }
                },
                {
                    opcode: 'beep',
                    text: 'beep',
                    blockType: BlockType.COMMAND
                }
            ],
            menus: {
                motorPorts: this._buildMenu(MOTOR_PORTS)
            }
        };
    }

    /**
     * Create data for a menu in scratch-blocks format, consisting of an array of objects with text and
     * value properties. The text is a translated string, and the value is one-indexed.
     * @param  {object[]} info - An array of info objects each having a name property.
     * @return {array} - An array of objects with text and value properties.
     * @private
     */
    _buildMenu (info) {
        return info.map((entry, index) => {
            const obj = {};
            obj.text = entry.name;
            obj.value = String(index + 1);
            return obj;
        });
    }

    motorTurnClockwise (args) {
        // Validate arguments
        const port = Cast.toNumber(args.PORT);
        const time = Cast.toNumber(args.TIME) * 1000;

        this._device.motorTurnClockwise(port, time);
    }

    motorTurnCounterClockwise (args) {
        // Validate arguments
        const port = Cast.toNumber(args.PORT);
        const time = Cast.toNumber(args.TIME) * 1000;

        this._device.motorTurnCounterClockwise(port, time);
    }

    beep () {
        return this._device.beep();
    }
}

module.exports = Scratch3Ev3Blocks;
<commit_msg>All EV3 blocks stubbed out with console logs for now.<commit_after>const ArgumentType = require('../../extension-support/argument-type');
const BlockType = require('../../extension-support/block-type');
const Cast = require('../../util/cast');
const log = require('../../util/log');
const Base64Util = require('../../util/base64-util');
const BTSession = require('../../io/BTSession');

/**
 * High-level primitives / constants used by the extension.
 * @type {object}
 */
const BTCommand = {
    LAYER: 0x00,
    NUM8: 0x81,
    NUM16: 0x82,
    NUM32: 0x83,
    COAST: 0x0,
    BRAKE: 0x1,
    LONGRAMP: 50,
    STEPSPEED: 0xAE,
    TIMESPEED: 0xAF,
    OUTPUTSTOP: 0xA3,
    OUTPUTRESET: 0xA2,
    STEPSPEEDSYNC: 0xB0,
    TIMESPEEDSYNC: 0xB1
};

/**
 * Array of accepted motor ports.
 * @note These should not be translated as they correspond to labels on
 *       the EV3 hub.
 * @type {array}
 */
const MOTOR_PORTS = [
    {
        name: 'A',
        value: 1
    },
    {
        name: 'B',
        value: 2
    },
    {
        name: 'C',
        value: 4
    },
    {
        name: 'D',
        value: 8
    }
];

/**
 * Array of accepted sensor ports.
 * @note These should not be translated as they correspond to labels on
 *       the EV3 hub.
 * @type {array}
 */
 // TODO: are these names/values correct?
const SENSOR_PORTS = [
    {
        name: '1',
        value: 1
    },
    {
        name: '2',
        value: 2
    },
    {
        name: '3',
        value: 3
    },
    {
        name: '4',
        value: 4
    }
];

class EV3 {

    constructor (runtime, extensionId) {

        /**
         * The Scratch 3.0 runtime used to trigger the green flag button.
         * @type {Runtime}
         * @private
         */
        this._runtime = runtime;

        this.connected = false;
        this.speed = 50;

        /**
         * The Bluetooth connection session for reading/writing device data.
         * @type {BTSession}
         * @private
         */
        this._bt = null;
        this._runtime.registerExtensionDevice(extensionId, this);

        // TODO: auto-connect temporary - until button is added
        this.startDeviceScan();
    }

    // TODO: keep here?
    /**
     * Called by the runtime when user wants to scan for a device.
     */
    startDeviceScan () {
        log.info('making a new BT session');
        this._bt = new BTSession(this._runtime, {
            majorDeviceClass: 8,
            minorDeviceClass: 1
        }, this._onSessionConnect.bind(this));
    }

    // TODO: keep here?
    /**
     * Called by the runtime when user wants to connect to a certain device.
     * @param {number} id - the id of the device to connect to.
     */
    connectDevice (id) {
        this._bt.connectDevice(id);
    }

    beep () {
        if (!this.connected) return;
        this._bt.sendMessage({
            message: 'DwAAAIAAAJQBgQKC6AOC6AM=',
            encoding: 'base64'
        });
    }

    motorTurnClockwise (port, time) {
        if (!this.connected) return;

        // Build up motor command
        const cmd = this._applyPrefix(0, this._motorCommand(
            BTCommand.TIMESPEED,
            port,
            time,
            this.speed,
            BTCommand.LONGRAMP
        ));

        // Send message
        this._bt.sendMessage({
            message: Base64Util.arrayBufferToBase64(cmd),
            encoding: 'base64'
        });

        // Yield for time
        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, time);
        });
    }

    motorTurnCounterClockwise (port, time) {
        if (!this.connected) return;

        // Build up motor command
        const cmd = this._applyPrefix(0, this._motorCommand(
            BTCommand.TIMESPEED,
            port,
            time,
            this.speed * -1,
            BTCommand.LONGRAMP
        ));

        // Send message
        this._bt.sendMessage({
            message: Base64Util.arrayBufferToBase64(cmd),
            encoding: 'base64'
        });

        // Yield for time
        return new Promise(resolve => {
            setTimeout(() => {
                resolve();
            }, time);
        });
    }

    _applyPrefix (n, cmd) {
        const len = cmd.length + 5;
        return [].concat(
            len & 0xFF,
            (len >> 8) & 0xFF,
            0x1,
            0x0,
            0x0,
            n,
            0x0,
            cmd
        );
    }

    /**
     * Generate a motor command in EV3 byte array format (CMD, LAYER, PORT,
     * SPEED, RAMP UP, RUN, RAMP DOWN, BREAKING TYPE)
     * @param  {string} command Motor command primitive (i.e. "prefix")
     * @param  {string} port    Port to address
     * @param  {number} n       Value to be passed to motor command
     * @param  {number} speed   Speed value
     * @param  {number} ramp    Ramp value
     * @return {array}          Byte array
     */
    _motorCommand (command, port, n, speed, ramp) {
        /**
         * Generate run values for a given input.
         * @param  {number} run Run input
         * @return {array}      Run values (byte array)
         */
        const getRunValues = function (run) {
            // If run duration is less than max 16-bit integer
            if (run < 0x7fff) {
                return [
                    BTCommand.NUM16,
                    run & 0xff,
                    (run >> 8) & 0xff
                ];
            }

            // Run forever
            return [
                BTCommand.NUM32,
                run & 0xff,
                (run >> 8) & 0xff,
                (run >> 16) & 0xff,
                (run >> 24) & 0xff
            ];
        };

        // If speed is less than zero, make it positive and multiply the input
        // value by -1
        if (speed < 0) {
            speed = -1 * speed;
            n = -1 * n;
        }

        // If the input value is less than 0
        const dir = (n < 0) ? 0x100 - speed : speed; // step negative or possitive
        n = Math.abs(n);

        // Setup motor run duration and ramping behavior
        let rampup = ramp;
        let rampdown = ramp;
        let run = n - ramp * 2;
        if (run < 0) {
            rampup = Math.floor(n / 2);
            run = 0;
            rampdown = n - rampup;
        }

        // Generate motor command
        const runcmd = getRunValues(run);
        return [
            command,
            BTCommand.LAYER,
            port,
            BTCommand.NUM8,
            dir & 0xff,
            BTCommand.NUM8,
            rampup
        ].concat(runcmd.concat([
            BTCommand.NUM8,
            rampdown,
            BTCommand.BRAKE
        ]));
    }

    _onSessionConnect () {
        log.info('bt device connected!');
        this.connected = true;
        // start reading data?
    }

}

class Scratch3Ev3Blocks {

    /**
     * The ID of the extension.
     * @return {string} the id
     */
    static get EXTENSION_ID () {
        return 'ev3';
    }

    /**
     * Creates a new instance of the EV3 extension.
     * @param  {object} runtime VM runtime
     * @constructor
     */
    constructor (runtime) {
        /**
         * The Scratch 3.0 runtime.
         * @type {Runtime}
         */
        this.runtime = runtime;

        // Create a new MicroBit device instance
        this._device = new EV3(this.runtime, Scratch3Ev3Blocks.EXTENSION_ID);
    }

    /**
     * Define the EV3 extension.
     * @return {object} Extension description.
     */
    getInfo () {
        return {
            id: Scratch3Ev3Blocks.EXTENSION_ID,
            name: 'LEGO MINDSTORMS EV3',
            iconURI: null,
            blocks: [
                {
                    opcode: 'motorTurnClockwise',
                    text: 'motor [PORT] turn clockwise for [TIME] seconds',
                    blockType: BlockType.COMMAND,
                    arguments: {
                        PORT: {
                            type: ArgumentType.STRING,
                            menu: 'motorPorts',
                            defaultValue: MOTOR_PORTS[0].value
                        },
                        TIME: {
                            type: ArgumentType.NUMBER,
                            defaultValue: 1
                        }
                    }
                },
                {
                    opcode: 'motorTurnCounterClockwise',
                    text: 'motor [PORT] turn counter for [TIME] seconds',
                    blockType: BlockType.COMMAND,
                    arguments: {
                        PORT: {
                            type: ArgumentType.STRING,
                            menu: 'motorPorts',
                            defaultValue: MOTOR_PORTS[0].value
                        },
                        TIME: {
                            type: ArgumentType.NUMBER,
                            defaultValue: 1
                        }
                    }
                },
                {
                    opcode: 'motorRotate',
                    text: 'motor [PORT] rotate [DEGREES] degrees',
                    blockType: BlockType.COMMAND,
                    arguments: {
                        PORT: {
                            type: ArgumentType.STRING,
                            menu: 'motorPorts',
                            defaultValue: MOTOR_PORTS[0].value
                        },
                        DEGREES: {
                            type: ArgumentType.NUMBER,
                            defaultValue: 90
                        }
                    }
                },
                {
                    opcode: 'motorSetPosition',
                    text: 'motor [PORT] set position [DEGREES] degrees',
                    blockType: BlockType.COMMAND,
                    arguments: {
                        PORT: {
                            type: ArgumentType.STRING,
                            menu: 'motorPorts',
                            defaultValue: MOTOR_PORTS[0].value
                        },
                        DEGREES: {
                            type: ArgumentType.NUMBER,
                            defaultValue: 90
                        }
                    }
                },
                {
                    opcode: 'motorSetPower',
                    text: 'motor [PORT] set power [POWER] %',
                    blockType: BlockType.COMMAND,
                    arguments: {
                        PORT: {
                            type: ArgumentType.STRING,
                            menu: 'motorPorts',
                            defaultValue: MOTOR_PORTS[0].value
                        },
                        POWER: {
                            type: ArgumentType.NUMBER,
                            defaultValue: 50
                        }
                    }
                },
                {
                    opcode: 'getMotorPosition',
                    text: 'motor [PORT] position',
                    blockType: BlockType.REPORTER,
                    arguments: {
                        PORT: {
                            type: ArgumentType.STRING,
                            menu: 'motorPorts',
                            defaultValue: MOTOR_PORTS[0].value
                        }
                    }
                },
                {
                    opcode: 'whenButtonPressed',
                    text: 'when button [PORT] pressed',
                    blockType: BlockType.HAT,
                    arguments: {
                        PORT: {
                            type: ArgumentType.STRING,
                            menu: 'sensorPorts',
                            defaultValue: SENSOR_PORTS[0].value
                        }
                    }
                },
                {
                    opcode: 'whenDistanceLessThan',
                    text: 'when distance < [DISTANCE]',
                    blockType: BlockType.HAT,
                    arguments: {
                        DISTANCE: {
                            type: ArgumentType.NUMBER,
                            defaultValue: 50
                        }
                    }
                },
                {
                    opcode: 'whenBrightnessLessThan',
                    text: 'when brightness < [DISTANCE]',
                    blockType: BlockType.HAT,
                    arguments: {
                        DISTANCE: {
                            type: ArgumentType.NUMBER,
                            defaultValue: 50
                        }
                    }
                },
                {
                    opcode: 'buttonPressed',
                    text: 'button [PORT] pressed?',
                    blockType: BlockType.BOOLEAN,
                    arguments: {
                        PORT: {
                            type: ArgumentType.STRING,
                            menu: 'sensorPorts',
                            defaultValue: SENSOR_PORTS[0].value
                        }
                    }
                },
                {
                    opcode: 'getDistance',
                    text: 'distance',
                    blockType: BlockType.REPORTER
                },
                {
                    opcode: 'getBrightness',
                    text: 'brightness',
                    blockType: BlockType.REPORTER
                },
                {
                    opcode: 'beep',
                    text: 'beep',
                    blockType: BlockType.COMMAND
                }
            ],
            menus: {
                motorPorts: this._buildMenu(MOTOR_PORTS),
                sensorPorts: this._buildMenu(SENSOR_PORTS)
            }
        };
    }

    /**
     * Create data for a menu in scratch-blocks format, consisting of an array of objects with text and
     * value properties. The text is a translated string, and the value is one-indexed.
     * @param  {object[]} info - An array of info objects each having a name property.
     * @return {array} - An array of objects with text and value properties.
     * @private
     */
    _buildMenu (info) {
        return info.map((entry, index) => {
            const obj = {};
            obj.text = entry.name;
            obj.value = String(index + 1);
            return obj;
        });
    }

    motorTurnClockwise (args) {
        // Validate arguments
        const port = Cast.toNumber(args.PORT);
        const time = Cast.toNumber(args.TIME) * 1000;

        this._device.motorTurnClockwise(port, time);
    }

    motorTurnCounterClockwise (args) {
        // Validate arguments
        const port = Cast.toNumber(args.PORT);
        const time = Cast.toNumber(args.TIME) * 1000;

        this._device.motorTurnCounterClockwise(port, time);
    }

    motorRotate (args) {
        log.info(`motor rotate: ${args}`);
    }

    motorSetPosition (args) {
        log.info(`motor set position: ${args}`);
    }

    motorSetPower (args) {
        log.info(`motor set power: ${args}`);
    }

    getMotorPosition (args) {
        log.info(`get motor position: ${args}`);
    }

    whenButtonPressed (args) {
        log.info(`when button pressed: ${args}`);
    }

    whenDistanceLessThan (args) {
        log.info(`when distance less than: ${args}`);
    }

    whenBrightnessLessThan (args) {
        log.info(`when brightness less than: ${args}`);
    }

    buttonPressed (args) {
        log.info(`button pressed: ${args}`);
    }

    getDistance () {
        log.info(`distance`);
    }

    getBrightness () {
        log.info(`brightness`);
    }

    beep () {
        return this._device.beep();
    }
}

module.exports = Scratch3Ev3Blocks;
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.u2.view',
  name: 'FObjectArrayView',
  extends: 'foam.u2.view.ArrayView',

  properties: [
    {
      class: 'Class',
      name: 'of'
    },
    {
      name: 'defaultNewItem',
      expression: function(of) {
        return foam.core.InterfaceModel.isInstance(of.model_)
          ? null
          : of.create();
      }
    },
    {
      name: 'valueView',
      expression: function(of) {
        return { class: 'foam.u2.DetailView' };
        /*
        return {
          class: 'foam.u2.view.CollapseableDetailView',
          view: {
            class: 'foam.u2.view.DraftDetailView',
            view: {
              class: 'foam.u2.view.FObjectView',
              of: of
            }
          }
        };
        */
      }
    }
  ]
});
<commit_msg>FOBjectArrayView copies 'of' from Property.<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.u2.view',
  name: 'FObjectArrayView',
  extends: 'foam.u2.view.ArrayView',

  properties: [
    {
      class: 'Class',
      name: 'of'
    },
    {
      name: 'defaultNewItem',
      expression: function(of) {
        return foam.core.InterfaceModel.isInstance(of.model_)
          ? null
          : of.create();
      }
    },
    {
      name: 'valueView',
      expression: function(of) {
        return { class: 'foam.u2.DetailView' };
        /*
        return {
          class: 'foam.u2.view.CollapseableDetailView',
          view: {
            class: 'foam.u2.view.DraftDetailView',
            view: {
              class: 'foam.u2.view.FObjectView',
              of: of
            }
          }
        };
        */
      }
    }
  ],

  methods: [
    function fromProperty(p) {
      this.SUPER(p);
      if ( ! this.of && p.of ) this.of = p.of;
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.u2.view',
  name: 'ReciprocalSearch',
  extends: 'foam.u2.Element', // TODO: make be a View

  requires: [
    'foam.u2.search.SearchManager'
  ],

  imports: [
    'dao'
  ],

  exports: [
    'as filterController',
    'as data'
  ],

  axioms: [
    foam.u2.CSS.create({
      code: function CSS() {/*
        ^count {
          font-size: 14pt;
          color: #555;
        }
      */}
    })
  ],

  properties: [
    {
      class: 'Class',
      name: 'of'
    },
    {
      name: 'data'
    },
    {
      class: 'Array',
      name: 'filters',
      factory: null,
      expression: function(dao) {
        var of = dao && dao.of;

        if ( ! of ) return [];

        return of.model_.searchColumns || of.model_.tableColumns ||
            of.getAxiomsByClass(foam.core.Property)
                .filter(function(p) { return ! p.hidden })
                .map(foam.core.Property.NAME.f);
      }
    },
    {
      class: 'Int',
      name: 'selectedCount'
    },
    {
      class: 'Int',
      name: 'totalCount'
    },
  ],

  methods: [
    function initE() {
      var self = this;

      this.dao.on.sub(this.updateTotalCount);
      this.updateTotalCount();

      this.
        add(this.slot(function(filters) {
          var searchManager = self.SearchManager.create({
            dao$: self.dao$,
            predicate$: self.data$
          });

//          searchManager.filteredDAO.on.sub(self.updateSelectedCount);
          searchManager.filteredDAO$.sub(self.updateSelectedCount);
          self.updateSelectedCount(0,0,0,searchManager.filteredDAO$);

          var e = this.E('div');

          e.onDetach(searchManager);

          e.forEach(filters, function(f) {
            // TODO: See if this can be cleaned up somehow, if searchView didn't require the proprety explicitly, or
            // could find the search manager via the context and add itself to that.
            var axiom = self.dao.of.getAxiomByName(f);
            var spec  = axiom.searchView;
            var view  = foam.u2.ViewSpec.createView(spec, { property: axiom, dao: self.dao }, this, this.__subSubContext__);

            searchManager.add(view);
            this
              .start()
                .addClass(self.myClass('filter-header'))
                .add(axiom.label)
              .end()
              .start(view)
                .addClass(self.myClass('filter'))
            .end()
          });

          return e;
        }, this.filters$))
        .start()
          .addClass(self.myClass('count'))
          // TODO: move formatting function to stdlib
          .add(self.selectedCount$.map(function(a) { return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); }))
          .entity('nbsp')
          .add('of')
          .entity('nbsp')
          .add(self.totalCount$.map(function(a) { return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); }))
          .entity('nbsp')
          .add('selected')
        .end()
        .start(this.CLEAR).style({float: 'right'}).end();
    },

    function addFilter(key) {
      this.filters = this.filters.concat(key);
    },

    function removeFilter(key) {
      this.filters = this.filters.filter(function(k) {
        return key !== k;
      });
    }
  ],

  actions: [
    {
      name: 'clear',
      code: function() {
        this.data = undefined;
        this.filters = this.filters.slice();
      }
    }
  ],

  /*
  reactions: [
    [ 'data', 'on', 'updateTotalCount' ]
  ],
  */

  listeners: [
    {
      name: 'updateTotalCount',
      isFramed: true,
      code: function() {
        this.dao.select(foam.mlang.sink.Count.create()).then(function(c) {
          this.totalCount = c.value;
        }.bind(this));
      }
    },
    {
      name: 'updateSelectedCount',
      isFramed: true,
      code: function(_, __, ___, dao) {
        dao.get().select(foam.mlang.sink.Count.create()).then(function(c) {
          this.selectedCount = c.value;
        }.bind(this));
      }
    }
  ]
});
<commit_msg>Fixed indentation.<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.u2.view',
  name: 'ReciprocalSearch',
  extends: 'foam.u2.Element', // TODO: make be a View

  requires: [
    'foam.u2.search.SearchManager'
  ],

  imports: [
    'dao'
  ],

  exports: [
    'as filterController',
    'as data'
  ],

  axioms: [
    foam.u2.CSS.create({
      code: function CSS() {/*
        ^count {
          font-size: 14pt;
          color: #555;
        }
      */}
    })
  ],

  properties: [
    {
      class: 'Class',
      name: 'of'
    },
    {
      name: 'data'
    },
    {
      class: 'Array',
      name: 'filters',
      factory: null,
      expression: function(dao) {
        var of = dao && dao.of;

        if ( ! of ) return [];

        return of.model_.searchColumns || of.model_.tableColumns ||
            of.getAxiomsByClass(foam.core.Property)
                .filter(function(p) { return ! p.hidden })
                .map(foam.core.Property.NAME.f);
      }
    },
    {
      class: 'Int',
      name: 'selectedCount'
    },
    {
      class: 'Int',
      name: 'totalCount'
    },
  ],

  methods: [
    function initE() {
      var self = this;

      this.dao.on.sub(this.updateTotalCount);
      this.updateTotalCount();

      this.
        add(this.slot(function(filters) {
          var searchManager = self.SearchManager.create({
            dao$: self.dao$,
            predicate$: self.data$
          });

//          searchManager.filteredDAO.on.sub(self.updateSelectedCount);
          searchManager.filteredDAO$.sub(self.updateSelectedCount);
          self.updateSelectedCount(0,0,0,searchManager.filteredDAO$);

          var e = this.E('div');

          e.onDetach(searchManager);

          e.forEach(filters, function(f) {
            // TODO: See if this can be cleaned up somehow, if searchView didn't require the proprety explicitly, or
            // could find the search manager via the context and add itself to that.
            var axiom = self.dao.of.getAxiomByName(f);
            var spec  = axiom.searchView;
            var view  = foam.u2.ViewSpec.createView(spec, { property: axiom, dao: self.dao }, this, this.__subSubContext__);

            searchManager.add(view);
            this
              .start()
                .addClass(self.myClass('filter-header'))
                .add(axiom.label)
              .end()
              .start(view)
                .addClass(self.myClass('filter'))
              .end();
          });

          return e;
        }, this.filters$))
        .start()
          .addClass(self.myClass('count'))
          // TODO: move formatting function to stdlib
          .add(self.selectedCount$.map(function(a) { return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); }))
          .entity('nbsp')
          .add('of')
          .entity('nbsp')
          .add(self.totalCount$.map(function(a) { return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); }))
          .entity('nbsp')
          .add('selected')
        .end()
        .start(this.CLEAR).style({float: 'right'}).end();
    },

    function addFilter(key) {
      this.filters = this.filters.concat(key);
    },

    function removeFilter(key) {
      this.filters = this.filters.filter(function(k) {
        return key !== k;
      });
    }
  ],

  actions: [
    {
      name: 'clear',
      code: function() {
        this.data = undefined;
        this.filters = this.filters.slice();
      }
    }
  ],

  /*
  reactions: [
    [ 'data', 'on', 'updateTotalCount' ]
  ],
  */

  listeners: [
    {
      name: 'updateTotalCount',
      isFramed: true,
      code: function() {
        this.dao.select(foam.mlang.sink.Count.create()).then(function(c) {
          this.totalCount = c.value;
        }.bind(this));
      }
    },
    {
      name: 'updateSelectedCount',
      isFramed: true,
      code: function(_, __, ___, dao) {
        dao.get().select(foam.mlang.sink.Count.create()).then(function(c) {
          this.selectedCount = c.value;
        }.bind(this));
      }
    }
  ]
});
<|endoftext|>