123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- import { GUI } from '../../examples/jsm/libs/lil-gui.module.min.js';
- {
- function outlineText( ctx, msg, x, y ) {
- ctx.strokeText( msg, x, y );
- ctx.fillText( msg, x, y );
- }
- function arrow( ctx, x1, y1, x2, y2, start, end, size ) {
- size = size || 1;
- const dx = x1 - x2;
- const dy = y1 - y2;
- const rot = - Math.atan2( dx, dy );
- const len = Math.sqrt( dx * dx + dy * dy );
- ctx.save();
- {
- ctx.translate( x1, y1 );
- ctx.rotate( rot );
- ctx.beginPath();
- ctx.moveTo( 0, 0 );
- ctx.lineTo( 0, - ( len - 10 * size ) );
- ctx.stroke();
- }
- ctx.restore();
- if ( start ) {
- arrowHead( ctx, x1, y1, rot, size );
- }
- if ( end ) {
- arrowHead( ctx, x2, y2, rot + Math.PI, size );
- }
- }
- function arrowHead( ctx, x, y, rot, size ) {
- ctx.save();
- {
- ctx.translate( x, y );
- ctx.rotate( rot );
- ctx.scale( size, size );
- ctx.translate( 0, - 10 );
- ctx.beginPath();
- ctx.moveTo( 0, 0 );
- ctx.lineTo( - 5, - 2 );
- ctx.lineTo( 0, 10 );
- ctx.lineTo( 5, - 2 );
- ctx.closePath();
- ctx.fill();
- }
- ctx.restore();
- }
- const THREE = {
- MathUtils: {
- radToDeg( rad ) {
- return rad * 180 / Math.PI;
- },
- degToRad( deg ) {
- return deg * Math.PI / 180;
- },
- },
- };
- class DegRadHelper {
- constructor( obj, prop ) {
- this.obj = obj;
- this.prop = prop;
- }
- get value() {
- return THREE.MathUtils.radToDeg( this.obj[ this.prop ] );
- }
- set value( v ) {
- this.obj[ this.prop ] = THREE.MathUtils.degToRad( v );
- }
- }
- function dot( x1, y1, x2, y2 ) {
- return x1 * x2 + y1 * y2;
- }
- function distance( x1, y1, x2, y2 ) {
- const dx = x1 - x2;
- const dy = y1 - y2;
- return Math.sqrt( dx * dx + dy * dy );
- }
- function normalize( x, y ) {
- const l = distance( 0, 0, x, y );
- if ( l > 0.00001 ) {
- return [ x / l, y / l ];
- } else {
- return [ 0, 0 ];
- }
- }
- function resizeCanvasToDisplaySize( canvas, pixelRatio = 1 ) {
- const width = canvas.clientWidth * pixelRatio | 0;
- const height = canvas.clientHeight * pixelRatio | 0;
- const needResize = canvas.width !== width || canvas.height !== height;
- if ( needResize ) {
- canvas.width = width;
- canvas.height = height;
- }
- return needResize;
- }
- const diagrams = {
- dotProduct: {
- create( info ) {
- const { elem } = info;
- const div = document.createElement( 'div' );
- div.style.position = 'relative';
- div.style.width = '100%';
- div.style.height = '100%';
- elem.appendChild( div );
- const ctx = document.createElement( 'canvas' ).getContext( '2d' );
- div.appendChild( ctx.canvas );
- const settings = {
- rotation: 0.3,
- };
- const gui = new GUI( { autoPlace: false } );
- gui.add( new DegRadHelper( settings, 'rotation' ), 'value', - 180, 180 ).name( 'rotation' ).onChange( render );
- gui.domElement.style.position = 'absolute';
- gui.domElement.style.top = '0';
- gui.domElement.style.right = '0';
- div.appendChild( gui.domElement );
- const darkColors = {
- globe: 'green',
- camera: '#AAA',
- base: '#DDD',
- label: '#0FF',
- };
- const lightColors = {
- globe: '#0C0',
- camera: 'black',
- base: '#000',
- label: 'blue',
- };
- const darkMatcher = window.matchMedia( '(prefers-color-scheme: dark)' );
- darkMatcher.addEventListener( 'change', render );
- function render() {
- const { rotation } = settings;
- const isDarkMode = darkMatcher.matches;
- const colors = isDarkMode ? darkColors : lightColors;
- const pixelRatio = window.devicePixelRatio;
- resizeCanvasToDisplaySize( ctx.canvas, pixelRatio );
- ctx.clearRect( 0, 0, ctx.canvas.width, ctx.canvas.height );
- ctx.save();
- {
- const width = ctx.canvas.width / pixelRatio;
- const height = ctx.canvas.height / pixelRatio;
- const min = Math.min( width, height );
- const half = min / 2;
- const r = half * 0.4;
- const x = r * Math.sin( - rotation );
- const y = r * Math.cos( - rotation );
- const camDX = x - 0;
- const camDY = y - ( half - 40 );
- const labelDir = normalize( x, y );
- const camToLabelDir = normalize( camDX, camDY );
- const dp = dot( ...camToLabelDir, ...labelDir );
- ctx.scale( pixelRatio, pixelRatio );
- ctx.save();
- {
- {
- ctx.translate( width / 2, height / 2 );
- ctx.beginPath();
- ctx.arc( 0, 0, half * 0.4, 0, Math.PI * 2 );
- ctx.fillStyle = colors.globe;
- ctx.fill();
- ctx.save();
- {
- ctx.fillStyle = colors.camera;
- ctx.translate( 0, half );
- ctx.fillRect( - 15, - 30, 30, 30 );
- ctx.beginPath();
- ctx.moveTo( 0, - 25 );
- ctx.lineTo( - 25, - 50 );
- ctx.lineTo( 25, - 50 );
- ctx.closePath();
- ctx.fill();
- }
- ctx.restore();
- ctx.save();
- {
- ctx.lineWidth = 4;
- ctx.strokeStyle = colors.camera;
- ctx.fillStyle = colors.camera;
- arrow( ctx, 0, half - 40, x, y, false, true, 2 );
- ctx.save();
- {
- ctx.strokeStyle = colors.label;
- ctx.fillStyle = colors.label;
- arrow( ctx, 0, 0, x, y, false, true, 2 );
- }
- ctx.restore();
- {
- ctx.lineWidth = 3;
- ctx.strokeStyle = 'black';
- ctx.fillStyle = dp < 0 ? 'white' : 'red';
- ctx.font = '20px sans-serif';
- ctx.textAlign = 'center';
- ctx.textBaseline = 'middle';
- outlineText( ctx, 'label', x, y );
- }
- }
- ctx.restore();
- }
- ctx.restore();
- }
- ctx.lineWidth = 3;
- ctx.font = '24px sans-serif';
- ctx.strokeStyle = 'black';
- ctx.textAlign = 'left';
- ctx.textBaseline = 'middle';
- ctx.save();
- {
- ctx.translate( width / 4, 80 );
- const textColor = dp < 0 ? colors.base : 'red';
- advanceText( ctx, textColor, 'dot( ' );
- ctx.save();
- {
- ctx.fillStyle = colors.camera;
- ctx.strokeStyle = colors.camera;
- ctx.rotate( Math.atan2( camDY, camDX ) );
- arrow( ctx, - 8, 0, 8, 0, false, true, 1 );
- }
- ctx.restore();
- advanceText( ctx, textColor, ' , ' );
- ctx.save();
- {
- ctx.fillStyle = colors.label;
- ctx.strokeStyle = colors.label;
- ctx.rotate( rotation + Math.PI * 0.5 );
- arrow( ctx, - 8, 0, 8, 0, false, true, 1 );
- }
- ctx.restore();
- advanceText( ctx, textColor, ` ) = ${dp.toFixed( 2 )}` );
- }
- ctx.restore();
- }
- ctx.restore();
- }
- render();
- window.addEventListener( 'resize', render );
- },
- },
- };
- function advanceText( ctx, color, str ) {
- ctx.fillStyle = color;
- ctx.fillText( str, 0, 0 );
- ctx.translate( ctx.measureText( str ).width, 0 );
- }
- [ ...document.querySelectorAll( '[data-diagram]' ) ].forEach( createDiagram );
- function createDiagram( base ) {
- const name = base.dataset.diagram;
- const info = diagrams[ name ];
- if ( ! info ) {
- throw new Error( `no diagram ${name}` );
- }
- info.create( { elem: base } );
- }
- }
|