Skip to content

Commit

Permalink
feat: add ellipse & rect in lesson9
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoiver committed Jun 20, 2024
1 parent d72c12d commit db9135d
Show file tree
Hide file tree
Showing 16 changed files with 833 additions and 413 deletions.
2 changes: 1 addition & 1 deletion packages/lesson_008/src/components/infinite-canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class InfiniteCanvas extends LitElement {
return html`
<sl-resize-observer>
<canvas></canvas>
<ic-zoom-toolbar-lesson8 zoom=${this.zoom}></ic-zoom-toolbar>
<ic-zoom-toolbar-lesson8 zoom=${this.zoom}></ic-zoom-toolbar-lesson8>
</sl-resize-observer>
`;
}
Expand Down
24 changes: 20 additions & 4 deletions packages/lesson_009/examples/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Canvas, Circle } from '../src';
import { Canvas, Circle, Ellipse, Rect } from '../src';

const $canvas = document.getElementById('canvas') as HTMLCanvasElement;
const resize = (width: number, height: number) => {
Expand All @@ -19,8 +19,7 @@ const canvas = await new Canvas({
'https://unpkg.com/@antv/[email protected]/dist/pkg/glsl_wgsl_compiler_bg.wasm',
}).initialized;

const circles: Circle[] = [];
for (let i = 0; i < 200; i++) {
for (let i = 0; i < 2000; i++) {
const fill = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(
Math.random() * 255,
)},${Math.floor(Math.random() * 255)})`;
Expand All @@ -34,7 +33,24 @@ for (let i = 0; i < 200; i++) {
batchable: true,
});
canvas.appendChild(circle);
circles.push(circle);

const ellipse = new Ellipse({
cx: Math.random() * 1000,
cy: Math.random() * 1000,
rx: Math.random() * 20,
ry: Math.random() * 20,
fill,
});
canvas.appendChild(ellipse);

const rect = new Rect({
x: Math.random() * 1000,
y: Math.random() * 1000,
width: Math.random() * 20,
height: Math.random() * 20,
fill,
});
canvas.appendChild(rect);

circle.addEventListener('pointerenter', () => {
circle.fill = 'red';
Expand Down
17 changes: 12 additions & 5 deletions packages/lesson_009/src/Canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
findZoomCeil,
findZoomFloor,
} from './plugins';
import { Group, IDENTITY_TRANSFORM, RBushNodeAABB, type Shape } from './shapes';
import { Group, IDENTITY_TRANSFORM, RBushNodeAABB, Shape } from './shapes';
import {
AsyncParallelHook,
SyncHook,
Expand Down Expand Up @@ -87,9 +87,13 @@ export class Canvas {
hooks: {
init: new SyncHook<[]>(),
initAsync: new AsyncParallelHook<[]>(),
beginFrame: new SyncHook<[{ all: Shape[], modified: Shape[], removed: Shape[] }]>(),
beginFrame: new SyncHook<
[{ all: Shape[]; modified: Shape[]; removed: Shape[] }]
>(),
render: new SyncHook<[Shape]>(),
endFrame: new SyncHook<[{ all: Shape[]; modified: Shape[]; removed: Shape[] }]>(),
endFrame: new SyncHook<
[{ all: Shape[]; modified: Shape[]; removed: Shape[] }]
>(),
destroy: new SyncHook<[]>(),
resize: new SyncHook<[number, number]>(),
pointerDown: new SyncHook<[InteractivePointerEvent]>(),
Expand Down Expand Up @@ -164,10 +168,13 @@ export class Canvas {
traverse(this.#root, (shape) => {
this.#shapesCurrentFrame.add(shape);

if (shape.transformDirtyFlag || (shape.renderable && shape.renderDirtyFlag)) {
if (
shape.transformDirtyFlag ||
(shape.renderable && shape.renderDirtyFlag)
) {
modified.push(shape);
this.#renderDirtyFlag = true;
}
}

shape.transform.updateTransform(
shape.parent ? shape.parent.transform : IDENTITY_TRANSFORM,
Expand Down
2 changes: 1 addition & 1 deletion packages/lesson_009/src/components/infinite-canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class InfiniteCanvas extends LitElement {
return html`
<sl-resize-observer>
<canvas></canvas>
<ic-zoom-toolbar-lesson8 zoom=${this.zoom}></ic-zoom-toolbar>
<ic-zoom-toolbar-lesson9 zoom=${this.zoom}></ic-zoom-toolbar-lesson9>
</sl-resize-observer>
`;
}
Expand Down
117 changes: 51 additions & 66 deletions packages/lesson_009/src/drawcalls/SDF.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
Program,
CompareFunction,
} from '@antv/g-device-api';
import { Circle } from '../shapes';
import { Circle, Ellipse, Rect, Shape } from '../shapes';
import { Drawcall, ZINDEX_FACTOR } from './Drawcall';
import { vert, frag } from '../shaders/sdf';
import { paddingMat3 } from '../utils';
Expand Down Expand Up @@ -249,83 +249,21 @@ export class SDF extends Drawcall {
const instancedData: number[] = [];
this.shapes.forEach((shape) => {
if (shape instanceof Circle) {
const {
cx,
cy,
r: radius,
fillRGB: { r: fr, g: fg, b: fb, opacity: fo },
strokeRGB: { r: sr, g: sg, b: sb, opacity: so },
strokeWidth,
opacity,
fillOpacity,
strokeOpacity,
} = shape;
instancedData.push(
cx,
cy,
radius,
radius,
fr / 255,
fg / 255,
fb / 255,
fo,
sr / 255,
sg / 255,
sb / 255,
so,
shape.globalRenderOrder / ZINDEX_FACTOR,
strokeWidth,
0,
0,
opacity,
fillOpacity,
strokeOpacity,
0,
);
instancedData.push(...this.generateBuffer(shape));
}
});
this.#instancedBuffer.setSubData(
0,
new Uint8Array(new Float32Array(instancedData).buffer),
);
} else {
const {
cx,
cy,
r: radius,
fillRGB: { r: fr, g: fg, b: fb, opacity: fo },
strokeRGB: { r: sr, g: sg, b: sb, opacity: so },
strokeWidth,
worldTransform,
opacity,
fillOpacity,
strokeOpacity,
} = this.shapes[0] as Circle;
const { worldTransform } = this.shapes[0];
this.#uniformBuffer.setSubData(
0,
new Uint8Array(
new Float32Array([
...paddingMat3(worldTransform.toArray(true)),
cx,
cy,
radius,
radius,
fr / 255,
fg / 255,
fb / 255,
fo,
sr / 255,
sg / 255,
sb / 255,
so,
this.shapes[0].globalRenderOrder / ZINDEX_FACTOR,
strokeWidth,
0,
0,
opacity,
fillOpacity,
strokeOpacity,
0,
...this.generateBuffer(this.shapes[0]),
]).buffer,
),
);
Expand Down Expand Up @@ -367,4 +305,51 @@ export class SDF extends Drawcall {
this.#bindings?.destroy();
}
}

private generateBuffer(shape: Shape) {
const {
fillRGB: { r: fr, g: fg, b: fb, opacity: fo },
strokeRGB: { r: sr, g: sg, b: sb, opacity: so },
strokeWidth,
opacity,
fillOpacity,
strokeOpacity,
} = shape;

let size: [number, number, number, number];
let type: number;
if (shape instanceof Circle) {
const { cx, cy, r } = shape;
size = [cx, cy, r, r];
type = 0;
} else if (shape instanceof Ellipse) {
const { cx, cy, rx, ry } = shape;
size = [cx, cy, rx, ry];
type = 1;
} else if (shape instanceof Rect) {
const { x, y, width, height } = shape;
size = [x, y, width, height];
type = 2;
}

return [
...size,
fr / 255,
fg / 255,
fb / 255,
fo,
sr / 255,
sg / 255,
sb / 255,
so,
this.shapes[0].globalRenderOrder / ZINDEX_FACTOR,
strokeWidth,
type,
0,
opacity,
fillOpacity,
strokeOpacity,
0,
];
}
}
60 changes: 48 additions & 12 deletions packages/lesson_009/src/events/FederatedEventTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@ import type EventEmitter from 'eventemitter3';
import type { Rectangle } from '@pixi/math';
import type { FederatedPointerEvent } from './FederatedPointerEvent';

export type PointerEvents =
| 'none'
| 'auto'
| 'stroke'
| 'fill'
| 'painted'
| 'visible'
| 'visiblestroke'
| 'visiblefill'
| 'visiblepainted'
| 'all'
| 'non-transparent-pixel';

/**
* The type of cursor to use when the mouse pointer is hovering over.
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
Expand Down Expand Up @@ -57,8 +70,34 @@ export type Cursor =
* @memberof events
*/
export interface FederatedOptions {
/** The cursor preferred when the mouse pointer is hovering over. */
cursor?: Cursor | string;
/**
* It sets under what circumstances (if any) a particular graphic element can become the target of pointer events.
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events
*/
pointerEvents: PointerEvents;

/**
* It sets the mouse cursor, if any, to show when the mouse pointer is over an element.
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
*/
cursor: Cursor | string;

/**
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the container's bounds.
*/
hitArea: Rectangle | undefined;

/**
* Whether this object is draggable. Used in {@link DragAndDrop} plugin.
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable
*/
draggable: boolean;

/**
* Whether this object is droppable. Used in {@link DragAndDrop} plugin.
*/
droppable: boolean;
}

/**
Expand All @@ -68,22 +107,19 @@ export interface FederatedOptions {
export interface FederatedEventTarget
extends EventEmitter,
EventTarget,
Required<FederatedOptions> {
FederatedOptions {
pointerEvents: PointerEvents;
hitArea: Rectangle;
cursor: Cursor | string;
draggable: boolean;
droppable: boolean;

/** The parent of this event target. */
readonly parent?: FederatedEventTarget;

/** The children of this event target. */
readonly children?: ReadonlyArray<FederatedEventTarget>;

/**
* Interaction shape. Children will be hit first, then this shape will be checked.
* Setting this will cause this shape to be checked in hit tests rather than the container's bounds.
*/
hitArea?: Rectangle;

draggable: boolean;
droppable: boolean;

/** Remove all listeners, or those of the specified event. */
removeAllListeners(event?: string | symbol): this;
}
Expand Down
14 changes: 14 additions & 0 deletions packages/lesson_009/src/shaders/sdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,20 @@ float sdf_circle(vec2 p, float r) {
return length(p) - r;
}
// @see http://www.iquilezles.org/www/articles/ellipsoids/ellipsoids.htm
float sdf_ellipse(vec2 p, vec2 r) {
float k0 = length(p / r);
float k1 = length(p / (r * r));
return k0 * (k0 - 1.0) / k1;
}
// @see https://www.shadertoy.com/view/4llXD7
float sdf_rounded_box(vec2 p, vec2 b, float r) {
p = abs(p) - b + r;
return length(max(p, 0.0)) + min(max(p.x, p.y), 0.0) - r;
}
void main() {
float strokeWidth;
vec4 fillColor;
Expand Down
Loading

0 comments on commit db9135d

Please sign in to comment.