Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce 'ViroCircle' shape #316

Open
oOMoeOo opened this issue Jun 9, 2018 · 4 comments
Open

Introduce 'ViroCircle' shape #316

oOMoeOo opened this issue Jun 9, 2018 · 4 comments

Comments

@oOMoeOo
Copy link

oOMoeOo commented Jun 9, 2018

Hi,

i'd like to share you some code that creates a simple polyline based circle. Maybe it could be the starting point for introducing ViroCircle into the core api. I think that supporting more svg elements like circle, path and so on would create a great benefit for simple geometry based ar experiences.

The types are based on flow.

`import React from 'react';
import {ViroPolyline} from 'react-viro';
import Point2D from '../../business/scene/Point2D';
import Polyline from '../../business/scene/Polyline';

export type Props = {
radius: number,
thickness: number,
radialSamples: number,
position?: number[],
rotation?: number[],
scale?: number[]
}

type State = {
circleLine: Polyline
}

export default class Circle extends React.Component<Props, State> {

props: Props;
state: State;

constructor(props) {
    super(props);
    this.state = {
        circleLine: this.computeCircleLine()
    }
}

render() {
    return (
        <ViroPolyline
            position={this.props.position}
            rotation={this.props.rotation}
            scale={this.props.scale}
            points={this.state.circleLine.toNumber3Array()}
            thickness={this.props.thickness}
        />
    );
}

computeCircleLine = (): Polyline => {
    let samples = 50;
    let angle = 2 * Math.PI / samples;
    let point = new Point2D(0, this.props.radius);

    const points: Point2D[] = [];

    // iterate to (including) samples to createFromNumber3Array a closed polyline
    for (let i = 0; i <= samples; i++) {
        points.push(point.copy());
        point.rotateLocal(angle);
    }

    return new Polyline(points);
};

}`

`export default class Point2D {
x: number;
y: number;

constructor(x: number = 0, y: number = 0) {
    this.x = x;
    this.y = y;
}

scale(scalar: number): Point2D {
    return this.copy().scaleLocal(scalar);
}

scaleLocal(scalar: number): Point2D {
    this.x *= scalar;
    this.y *= scalar;
}

rotate(angle: number): Point2D {
    return this.copy().rotateLocal(angle);
}

rotateLocal(angle: number): Point2D {
    let sin = Math.sin(angle);
    let cos = Math.cos(angle);
    let tmpX = this.x * cos - this.y * sin;
    let tmpY = this.y * cos + this.x * sin;
    this.x = tmpX;
    this.y = tmpY;
    return this;
}

distanceTo(point: Point2D): number {
    let dx = point.x - this.x;
    let dy = point.y - this.y;
    return Math.sqrt(dx * dx + dy * dy);
}

toArray2D(): number[] {
    return [this.x, this.y];
}

toArray3D(): number[] {
    return [this.x, this.y, 0];
}

copy(): Point2D {
    return new Point2D(this.x, this.y);
}


/**
 * Computes the angle of the triangle spanned by p1 - this - p2
 *
 * @param a
 * @param c
 */
angle(p1: Point2D, p2: Point2D): number {
    const a = this.distanceTo(p2); // a
    const b = p1.distanceTo(p2); // b
    const c = p1.distanceTo(this); // c
    let cos = ((a * a) + (c * c) - (b * b)) / (2 * a * c);
    console.log(180 / Math.PI);
    return Math.acos(cos) * 180 / Math.PI;
}

static angleBetween(a: Point2D, b: Point2D, c: Point2D) {
    return b.angle(a, c);
}

}`

@VikAdvani
Copy link
Contributor

Hi @oOMoeOo, this is great, thank you for this and thanks for the code! We have been discussing adding more primitives as well as a path functionality for more complex curves. When we do include those components we will update this issue to let you know.

@javc86
Copy link

javc86 commented Nov 21, 2018

Hi @oOMoeOo, What is the code for ../../business/scene/Polyline? I only see the Circle and Point2D components.

@oOMoeOo
Copy link
Author

oOMoeOo commented Nov 21, 2018

I cannot share you the full code but this snippet should make it working. You don't need the polyline class. It is just a wrapper for a point array with some utility functions.

`
export default class Polyline {
points: Array;
bounds: Bounds;

/**
 * Creates a polyline assuming two dimensional points.
 * @param points The points representing the polyline.
 */
constructor(points: Array<Point2D> = []) {
    this.points = points;
    this.bounds = new Bounds();
    this.updateBounds();
}

static createFromString = (value: string): Polyline => {
    if (value && value.length > 0) {
        let points = value.split(' ').map(point => {
            let coords = point.split(',');
            return new Point2D(Number(coords[0]), Number(coords[1]));
        });
        return new Polyline(points);
    }
    return null;
};

/**
 * Creates a polyline from a flat array of points.
 * @param points The array of flat points.
 * @param dimension The dimension of the points within the point array.
 * @returns {Polyline}
 */
static createFromNumberArray(points: Array<number>, dimension: number = 2) {
    const polyPoints: Array<Point2D> = [];
    for (let i = 0; i < points.length; i += dimension) {
        polyPoints.push(new Point2D(points[i], points[i + 1]));
    }
    return new Polyline(polyPoints);
}

static createFromNumber3Array(points: Array<Array<number>>, scale: number = 1) {
    const polyPoints: Array<Point2D> = [];
    for (let point of points) {
        polyPoints.push(new Point2D(point[0] * scale, point[1] * scale));
    }
    return new Polyline(polyPoints);
}

get numPoints(): number {
    return this.points.length;
}

getPointAt(index: number): Point2D {
    return this.points[index];
}

addPoint(point: Point2D) {
    this.points.push(point);
}

toNumber3Array(): Array<Array<number>> {
    let points = [];
    for (let point of this.points) {
        points.push(point.toArray3D());
    }
    return points;
}

getBounds(): Bounds {
    if (!this.bounds.valid) {
        this.updateBounds();
    }
    return this.bounds;
}

updateBounds() {
    this.bounds.invalidate();
    this.points.forEach(point => this.bounds.mergePoint(point));
}

}
`

@javc86
Copy link

javc86 commented Nov 21, 2018

Thanks @oOMoeOo, I'm goint to testing your code for my app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants