├── .editorconfig ├── .github └── workflows │ └── tapspace-ci.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── dist └── .gitkeep ├── docs ├── api │ ├── index.md │ ├── v1 │ │ └── index.md │ └── v2 │ │ ├── generate.js │ │ ├── index.md │ │ ├── intro.js │ │ ├── tapspace_class_chart.png │ │ ├── tapspace_class_chart.svg │ │ ├── tapspace_interaction_chart.png │ │ └── tapspace_interaction_chart.svg ├── collaborators │ ├── CoDHR_logo.png │ ├── lincs_logo.png │ ├── tampere_university_logo.png │ ├── texas_am_university_logo.png │ └── university_of_guelph_logo.png ├── dev │ ├── design.md │ ├── docstyle.md │ └── index.md ├── examples │ ├── assets │ │ ├── Ajax-loader.gif │ │ ├── arrowheads.png │ │ ├── chellah_nw.jpg │ │ ├── chellah_star.jpg │ │ ├── food-drink.css │ │ ├── food-drink.json │ │ ├── food-drink.png │ │ ├── gear11.png │ │ ├── gear18.png │ │ ├── gearhandle.png │ │ ├── ghoulog.js │ │ ├── go-stone-black.png │ │ ├── go-stone-white.png │ │ ├── marrakech_knot.jpg │ │ ├── marrakech_mosaic.jpg │ │ ├── marrakech_sun.jpg │ │ ├── openmoji-cherry.png │ │ ├── oudaya_door.jpg │ │ ├── rabat_sand.jpg │ │ ├── square.png │ │ ├── tile00.png │ │ ├── tile01.png │ │ ├── tile02.png │ │ ├── tile03.png │ │ ├── tile04.png │ │ ├── tile05.png │ │ ├── tile06.png │ │ ├── tile07.png │ │ └── wood-cracked.jpg │ ├── book │ │ ├── index.html │ │ └── preview.png │ ├── canvas │ │ ├── index.html │ │ └── preview.jpg │ ├── cielab │ │ ├── index.html │ │ └── preview.png │ ├── fruitfractal │ │ ├── index.html │ │ └── preview.jpg │ ├── gears │ │ ├── index.html │ │ └── preview.jpg │ ├── go │ │ ├── index.html │ │ └── preview.jpg │ ├── grid │ │ ├── index.html │ │ └── preview.jpg │ ├── html │ │ ├── index.html │ │ └── preview.jpg │ ├── infinity │ │ ├── index.html │ │ └── preview.jpg │ ├── logo │ │ └── index.html │ ├── minimal │ │ ├── index.html │ │ └── preview.png │ ├── modes │ │ ├── index.html │ │ └── preview.jpg │ ├── password │ │ ├── index.html │ │ └── preview.jpg │ ├── pixels │ │ ├── index.html │ │ └── preview.jpg │ ├── semanticzoom │ │ ├── index.html │ │ └── preview.jpg │ ├── server.js │ ├── tiles │ │ ├── index.html │ │ └── preview.jpg │ ├── treeloader │ │ ├── index.html │ │ └── preview.png │ ├── tunnel │ │ ├── index.html │ │ └── preview.jpg │ ├── tutorial │ │ └── index.html │ └── visjs │ │ ├── index.html │ │ └── preview.png ├── glossary │ └── v2 │ │ └── index.md ├── index.md ├── taaspace-logo-128.png ├── taaspace-logo-256.png ├── taaspace-logo-6000.png ├── tapspace-banner-2018.png ├── tapspace-banner-2022.png ├── tapspace-banner-2023.png └── tutorial │ ├── index.md │ ├── v1 │ └── index.md │ └── v2 │ ├── coordinates_directions_512.png │ └── index.md ├── features ├── assets │ ├── basis_2d_size800_unit500_orig100.png │ ├── grid_size400.png │ ├── midjourney_creatures_cooking_question.jpg │ ├── midjourney_damp_giants_came_back_to_light.jpg │ ├── midjourney_steal_a_river_to_stare_some_woodland.jpg │ ├── midjourney_stretching_honor_guard_try_catch_wall_warriors_doom.jpg │ ├── midjourney_the_way_of_painful_cheese_and_thin_herbs.jpg │ └── placeholder400.png ├── components-basis.html ├── components-edges.html ├── components-element.html ├── components-ordering.html ├── components-pixel.html ├── components-stress-test.html ├── effects-press.html ├── geometry-animate.html ├── geometry-background.html ├── geometry-cards.html ├── geometry-infinity.html ├── geometry-matching-points.html ├── geometry-matching-sizes.html ├── geometry-measuring.html ├── geometry-uniform-scaling.html ├── interaction-approach.html ├── interaction-content.html ├── interaction-drag.html ├── interaction-hold.html ├── interaction-rotate.html ├── interaction-slide.html ├── interaction-tap.html ├── loaders-async.html ├── loaders-backward.html ├── loaders-forward.html ├── loaders-fractal.html ├── loaders-nearest.html ├── loaders-nested.html ├── viewport-controls.html ├── viewport-focus.html ├── viewport-limits.html ├── viewport-navigation-scalefree.html ├── viewport-pannable.html ├── viewport-responsive.html ├── viewport-rotatable.html └── viewport-zoomable.html ├── index.js ├── lib ├── capturers │ ├── CameraCapturer │ │ ├── createCameraHandler.js │ │ └── index.js │ ├── Capturer │ │ └── index.js │ ├── GestureCapturer │ │ ├── Sensor │ │ │ ├── index.js │ │ │ ├── isEventAffine.js │ │ │ ├── oncontextmenu.js │ │ │ ├── onpointercancel.js │ │ │ ├── onpointerdown.js │ │ │ ├── onpointermove.js │ │ │ ├── onpointerup.js │ │ │ ├── registerModifiers.js │ │ │ ├── unbind.js │ │ │ └── update.js │ │ ├── convertToActive.js │ │ ├── emitGestureCancel.js │ │ ├── emitGestureEnd.js │ │ ├── emitGestureMove.js │ │ ├── emitGestureStart.js │ │ ├── findAffineTarget.js │ │ ├── getDeltaOrigin.js │ │ ├── getPointersAverage.js │ │ ├── index.js │ │ ├── transitFreedom.js │ │ └── transitPointers.js │ ├── KeyboardCapturer │ │ ├── createKeydownHandler.js │ │ ├── createKeyupHandler.js │ │ └── index.js │ ├── ResizeCapturer │ │ ├── createResizeHandler.js │ │ └── index.js │ ├── WheelCapturer │ │ ├── createWheelHandler.js │ │ ├── index.js │ │ └── isEventAffine.js │ └── index.js ├── components │ ├── Animatable │ │ ├── animate.js │ │ ├── animateOnce.js │ │ ├── cancelAnimation.js │ │ ├── dom │ │ │ └── applyTransition.js │ │ └── index.js │ ├── Arc │ │ ├── create.js │ │ ├── getLength.js │ │ ├── getRadius.js │ │ ├── index.js │ │ └── setPoints.js │ ├── BlockComponent │ │ ├── atBottomLeft.js │ │ ├── atBottomMid.js │ │ ├── atBottomRight.js │ │ ├── atMidLeft.js │ │ ├── atMidMid.js │ │ ├── atMidRight.js │ │ ├── atNorm.js │ │ ├── atTopLeft.js │ │ ├── atTopMid.js │ │ ├── atTopRight.js │ │ ├── getArea.js │ │ ├── getBoundingBox.js │ │ ├── getBoundingCircle.js │ │ ├── getDiameter.js │ │ ├── getHeight.js │ │ ├── getInnerSquare.js │ │ ├── getSize.js │ │ ├── getWidth.js │ │ ├── index.js │ │ ├── moveCenterTo.js │ │ ├── normAt.js │ │ ├── scaleToFill.js │ │ ├── scaleToFit.js │ │ ├── scaleToHeight.js │ │ └── scaleToWidth.js │ ├── Component │ │ ├── addChild.js │ │ ├── addClass.js │ │ ├── addLink.js │ │ ├── at.js │ │ ├── atAnchor.js │ │ ├── bringAbove.js │ │ ├── bringToFront.js │ │ ├── createBasis.js │ │ ├── createDirection.js │ │ ├── createDistance.js │ │ ├── createOrientation.js │ │ ├── createVector.js │ │ ├── dom │ │ │ ├── findAffineAncestor.js │ │ │ └── isAffine.js │ │ ├── findCommonAncestor.js │ │ ├── followLink.js │ │ ├── getAncestors.js │ │ ├── getBasis.js │ │ ├── getBasisAt.js │ │ ├── getChildren.js │ │ ├── getDescendants.js │ │ ├── getElement.js │ │ ├── getLeaves.js │ │ ├── getOrientation.js │ │ ├── getParent.js │ │ ├── getRoot.js │ │ ├── getScale.js │ │ ├── getTransitionFrom.js │ │ ├── getTransitionTo.js │ │ ├── getTransitionToParent.js │ │ ├── getTransitionToParentOf.js │ │ ├── getViewport.js │ │ ├── hasClass.js │ │ ├── hasLink.js │ │ ├── index.js │ │ ├── isLeaf.js │ │ ├── isRoot.js │ │ ├── prependChild.js │ │ ├── remove.js │ │ ├── removeChild.js │ │ ├── removeClass.js │ │ ├── removeLink.js │ │ ├── removeLinks.js │ │ ├── replaceChild.js │ │ ├── replaceParent.js │ │ ├── requestIdle.js │ │ ├── sendBelow.js │ │ ├── sendToBack.js │ │ ├── setId.js │ │ ├── setParent.js │ │ └── sortByDepth.js │ ├── Composite │ │ ├── getBoundingBox.js │ │ ├── getBoundingCircle.js │ │ ├── getSize.js │ │ └── index.js │ ├── CustomControl │ │ ├── create.js │ │ ├── html.js │ │ └── index.js │ ├── Edge │ │ ├── atEnd.js │ │ ├── atStart.js │ │ ├── create.js │ │ ├── getBoundingBox.js │ │ ├── getLength.js │ │ ├── index.js │ │ ├── renderTransform.js │ │ ├── setPoints.js │ │ ├── setWidth.js │ │ └── trimPoints.js │ ├── FrameComponent │ │ ├── atNorm.js │ │ ├── fitShape.js │ │ ├── getHeight.js │ │ ├── getSize.js │ │ ├── getWidth.js │ │ ├── index.js │ │ ├── matchPixelSize.js │ │ ├── matchSize.js │ │ ├── normAt.js │ │ ├── resizeTo.js │ │ ├── setHeight.js │ │ ├── setSize.js │ │ ├── setWidth.js │ │ ├── transformToFill.js │ │ └── transformToFit.js │ ├── Hyperspace │ │ ├── atAnchor.js │ │ ├── commit.js │ │ ├── index.js │ │ ├── renderTransform.js │ │ ├── requestIdle.js │ │ ├── rotateBy.js │ │ ├── scaleBy.js │ │ ├── transformBy.js │ │ └── translateBy.js │ ├── Interactive │ │ ├── addInteraction.js │ │ ├── capturer.js │ │ ├── converter.js │ │ ├── converters │ │ │ └── MouseConverter │ │ │ │ ├── index.js │ │ │ │ ├── start.js │ │ │ │ └── stop.js │ │ ├── focus.js │ │ ├── focusable.js │ │ ├── getCapturer.js │ │ ├── getInteraction.js │ │ ├── hasCapturer.js │ │ ├── hasInteraction.js │ │ ├── index.js │ │ ├── removeAllInteractions.js │ │ ├── removeInteraction.js │ │ ├── setContentInput.js │ │ ├── setPointerAction.js │ │ └── stopCapturer.js │ ├── Item │ │ ├── approachable.js │ │ ├── create.js │ │ ├── disable.js │ │ ├── draggable.js │ │ ├── focus.js │ │ ├── holdable.js │ │ ├── html.js │ │ ├── index.js │ │ ├── resizable.js │ │ ├── rotatable.js │ │ ├── scalable.js │ │ ├── slidable.js │ │ └── tappable.js │ ├── Node │ │ ├── create.js │ │ ├── getBoundingBox.js │ │ ├── getBoundingCircle.js │ │ ├── getDiameter.js │ │ ├── getRadius.js │ │ └── index.js │ ├── Space │ │ ├── create.js │ │ └── index.js │ ├── Transformer │ │ ├── atAnchor.js │ │ ├── dom │ │ │ ├── applyTransform2d.js │ │ │ ├── applyTransform3d.js │ │ │ └── applyTransformOrigin.js │ │ ├── getDistanceTo.js │ │ ├── getPosition.js │ │ ├── getVectorTo.js │ │ ├── index.js │ │ ├── match.js │ │ ├── matchBasis.js │ │ ├── matchPoint.js │ │ ├── patch │ │ │ └── counterTransformOrigin.js │ │ ├── renderTransform.js │ │ ├── rotateBy.js │ │ ├── rotateByDegrees.js │ │ ├── scaleBy.js │ │ ├── setAnchor.js │ │ ├── setBasis.js │ │ ├── setOrientation.js │ │ ├── setScale.js │ │ ├── snapGrid.js │ │ ├── snapPixels.js │ │ ├── transformBy.js │ │ ├── translateBy.js │ │ └── translateTo.js │ ├── Viewport │ │ ├── addChild.js │ │ ├── addControl.js │ │ ├── animateOnce.js │ │ ├── atNorm.js │ │ ├── atPage.js │ │ ├── atPageFn.js │ │ ├── balanceOrientation.js │ │ ├── create.js │ │ ├── findSingular.js │ │ ├── focus.js │ │ ├── getAspectRatio.js │ │ ├── getControls.js │ │ ├── getHeight.js │ │ ├── getHyperspace.js │ │ ├── getItemAt.js │ │ ├── getNavigationBasis.js │ │ ├── getSize.js │ │ ├── getSpaces.js │ │ ├── getWidth.js │ │ ├── index.js │ │ ├── limitTo.js │ │ ├── measureAll.js │ │ ├── measureDilation.js │ │ ├── measureGroup.js │ │ ├── measureMany.js │ │ ├── measureNearest.js │ │ ├── measureOne.js │ │ ├── normAt.js │ │ ├── pannable.js │ │ ├── prependChild.js │ │ ├── removeChild.js │ │ ├── removeControl.js │ │ ├── renderTransform.js │ │ ├── requestIdle.js │ │ ├── responsive.js │ │ ├── rotatable.js │ │ ├── rotateBy.js │ │ ├── scaleBy.js │ │ ├── setMeasureMode.js │ │ ├── setNavigationBasis.js │ │ ├── setOrientation.js │ │ ├── snapPixels.js │ │ ├── tappable.js │ │ ├── toPage.js │ │ ├── transformBy.js │ │ ├── translateBy.js │ │ ├── translateTo.js │ │ ├── zoomTo.js │ │ ├── zoomToFill.js │ │ ├── zoomToFit.js │ │ └── zoomable.js │ ├── ViewportControl │ │ └── index.js │ ├── ViewportControls │ │ ├── adapt.js │ │ └── index.js │ ├── ZoomControl │ │ └── index.js │ └── index.js ├── effects │ ├── index.js │ └── press │ │ └── index.js ├── geometry │ ├── Area │ │ ├── changeBasis.js │ │ ├── getRaw.js │ │ ├── index.js │ │ ├── projectTo.js │ │ └── transitRaw.js │ ├── Basis │ │ ├── almostEqual.js │ │ ├── at.js │ │ ├── changeBasis.js │ │ ├── createDirection.js │ │ ├── createDistance.js │ │ ├── createVector.js │ │ ├── equal.js │ │ ├── fromPoints.js │ │ ├── getMatchedOuter.js │ │ ├── getOrientation.js │ │ ├── getRaw.js │ │ ├── getScale.js │ │ ├── getTransformTo.js │ │ ├── index.js │ │ ├── offset.js │ │ ├── outerOffset.js │ │ ├── polarOffset.js │ │ ├── rotateBy.js │ │ ├── rotateByDegrees.js │ │ ├── scaleBy.js │ │ ├── transformBy.js │ │ ├── transitRaw.js │ │ ├── transitRawOuter.js │ │ └── translateBy.js │ ├── Box │ │ ├── almostEqual.js │ │ ├── at.js │ │ ├── atCenter.js │ │ ├── atNorm.js │ │ ├── changeBasis.js │ │ ├── detectCollision.js │ │ ├── equal.js │ │ ├── fromBoxes.js │ │ ├── fromPoints.js │ │ ├── getArea.js │ │ ├── getBoundingBox.js │ │ ├── getBoundingCircle.js │ │ ├── getBoundingSphere.js │ │ ├── getDepth.js │ │ ├── getDiagonal.js │ │ ├── getHeight.js │ │ ├── getInnerSquare.js │ │ ├── getRaw.js │ │ ├── getSize.js │ │ ├── getVolume.js │ │ ├── getWidth.js │ │ ├── index.js │ │ ├── normAt.js │ │ ├── projectTo.js │ │ ├── resizeTo.js │ │ ├── rotateBy.js │ │ ├── scaleBy.js │ │ ├── transitRaw.js │ │ └── translateBy.js │ ├── Circle │ │ ├── almostEqual.js │ │ ├── atArc.js │ │ ├── atCenter.js │ │ ├── changeBasis.js │ │ ├── detectCollision.js │ │ ├── equal.js │ │ ├── fromPoints.js │ │ ├── getArea.js │ │ ├── getBoundingBox.js │ │ ├── getCollisionArea.js │ │ ├── getDiameter.js │ │ ├── getRadius.js │ │ ├── getRaw.js │ │ ├── getSize.js │ │ ├── index.js │ │ ├── offset.js │ │ ├── scaleBy.js │ │ ├── transitRaw.js │ │ └── translateBy.js │ ├── Direction │ │ ├── almostEqual.js │ │ ├── changeBasis.js │ │ ├── equal.js │ │ ├── fromSpherical.js │ │ ├── fromVector.js │ │ ├── getRaw.js │ │ ├── getVector.js │ │ ├── index.js │ │ └── transitRaw.js │ ├── Distance │ │ ├── changeBasis.js │ │ ├── equal.js │ │ ├── getNumber.js │ │ ├── getRaw.js │ │ ├── getVector.js │ │ ├── index.js │ │ ├── isAlmostEqual.js │ │ ├── isGreaterThan.js │ │ ├── isLessThan.js │ │ ├── max.js │ │ ├── min.js │ │ ├── projectTo.js │ │ ├── scaleBy.js │ │ └── transitRaw.js │ ├── Grid │ │ └── index.js │ ├── Line │ │ └── index.js │ ├── Orientation │ │ ├── almostEqual.js │ │ ├── changeBasis.js │ │ ├── equal.js │ │ ├── fromVectorBasis.js │ │ ├── getRaw.js │ │ ├── getUnitX.js │ │ ├── getUnitY.js │ │ ├── getUnitZ.js │ │ ├── index.js │ │ ├── transitRaw.js │ │ └── transitRawOuter.js │ ├── Path │ │ ├── changeBasis.js │ │ ├── getRaw.js │ │ ├── index.js │ │ └── transitRaw.js │ ├── Point │ │ ├── addVector.js │ │ ├── almostEqual.js │ │ ├── changeBasis.js │ │ ├── equal.js │ │ ├── fromAverage.js │ │ ├── getDirectionTo.js │ │ ├── getDistanceTo.js │ │ ├── getRaw.js │ │ ├── getVectorTo.js │ │ ├── homothety.js │ │ ├── index.js │ │ ├── offset.js │ │ ├── polarOffset.js │ │ ├── projectTo.js │ │ ├── round.js │ │ ├── transformBy.js │ │ ├── transitRaw.js │ │ ├── transitRawOuter.js │ │ └── translateBy.js │ ├── Polygon │ │ └── index.js │ ├── Ray │ │ ├── at.js │ │ ├── create.js │ │ ├── getDistanceToPoint.js │ │ └── index.js │ ├── Scale │ │ ├── changeBasis.js │ │ ├── getRaw.js │ │ ├── index.js │ │ ├── scaleBy.js │ │ ├── transitRaw.js │ │ └── transitRawOuter.js │ ├── Size │ │ ├── almostEqual.js │ │ ├── changeBasis.js │ │ ├── equal.js │ │ ├── getArea.js │ │ ├── getRaw.js │ │ ├── index.js │ │ ├── normAt.js │ │ ├── scaleBy.js │ │ └── transitRaw.js │ ├── Sphere │ │ ├── almostEqual.js │ │ ├── atCenter.js │ │ ├── changeBasis.js │ │ ├── detectCollision.js │ │ ├── equal.js │ │ ├── fromPoints.js │ │ ├── getBoundingBox.js │ │ ├── getDiameter.js │ │ ├── getRadius.js │ │ ├── getRaw.js │ │ ├── getSize.js │ │ ├── getVolume.js │ │ ├── index.js │ │ ├── offset.js │ │ ├── scaleBy.js │ │ ├── transitRaw.js │ │ └── translateBy.js │ ├── Transform │ │ ├── changeBasis.js │ │ ├── estimate.js │ │ ├── fromFeatures.js │ │ ├── getRaw.js │ │ ├── getRotation.js │ │ ├── getTranslation.js │ │ ├── getVector.js │ │ ├── index.js │ │ ├── inverse.js │ │ └── transitRaw.js │ ├── Vector │ │ ├── add.js │ │ ├── almostEqual.js │ │ ├── changeBasis.js │ │ ├── copy.js │ │ ├── cross.js │ │ ├── difference.js │ │ ├── dot.js │ │ ├── equal.js │ │ ├── fromAverage.js │ │ ├── fromPolar.js │ │ ├── fromSpherical.js │ │ ├── getDirection.js │ │ ├── getDistance.js │ │ ├── getRaw.js │ │ ├── index.js │ │ ├── negate.js │ │ ├── normalize.js │ │ ├── rotateBy.js │ │ ├── scaleBy.js │ │ ├── transformBy.js │ │ └── transitRaw.js │ ├── Volume │ │ ├── changeBasis.js │ │ ├── getRaw.js │ │ ├── index.js │ │ └── transitRaw.js │ └── index.js ├── index.js ├── interaction │ ├── Approach │ │ └── index.js │ ├── Hold │ │ └── index.js │ ├── KeyboardPan │ │ ├── index.js │ │ ├── onkeydown.js │ │ └── update.js │ ├── KeyboardZoom │ │ ├── applyTransform.js │ │ ├── index.js │ │ └── onkeydown.js │ ├── Pinch │ │ ├── applyTransform.js │ │ ├── disableDilation.js │ │ ├── disableRotation.js │ │ ├── disableTranslation.js │ │ ├── enableDilation.js │ │ ├── enableRotation.js │ │ ├── enableTranslation.js │ │ ├── getFreedom.js │ │ ├── hasAnyFreedom.js │ │ ├── index.js │ │ └── unbind.js │ ├── RealignView │ │ └── index.js │ ├── Resize │ │ └── index.js │ ├── Slide │ │ └── index.js │ ├── Tap │ │ ├── bind.js │ │ ├── index.js │ │ ├── unbind.js │ │ └── update.js │ ├── WheelPan │ │ └── index.js │ ├── WheelRotate │ │ └── index.js │ ├── WheelZoom │ │ ├── index.js │ │ └── onwheel.js │ └── index.js ├── loaders │ ├── TreeLoader │ │ ├── addSpace.js │ │ ├── closeAll.js │ │ ├── closeChild.js │ │ ├── closeChildren.js │ │ ├── closeNeighbors.js │ │ ├── closeParent.js │ │ ├── closeSpace.js │ │ ├── countSpaces.js │ │ ├── getFrontier.js │ │ ├── getSpace.js │ │ ├── getSpaces.js │ │ ├── hasSpace.js │ │ ├── index.js │ │ ├── initSpace.js │ │ ├── openChild.js │ │ ├── openChildren.js │ │ ├── openNeighbors.js │ │ ├── openParent.js │ │ ├── remapChildren.js │ │ ├── remapParent.js │ │ ├── removeSpace.js │ │ └── utils │ │ │ └── treeDistance.js │ ├── index.js │ └── loadImages.js ├── metrics │ ├── Measurement │ │ ├── getVisualDistance.js │ │ └── index.js │ └── index.js ├── style.js └── version.js ├── package.json ├── test ├── components │ ├── Arc │ │ ├── getLength.html │ │ └── index.mjs │ ├── Component │ │ ├── findCommonAncestor.html │ │ ├── geometryCreation.html │ │ ├── index.mjs │ │ ├── prependChild.html │ │ ├── removeChild.html │ │ ├── replaceChild.html │ │ ├── replaceParent.html │ │ ├── setId.html │ │ └── setParent.html │ ├── FrameComponent │ │ ├── index.mjs │ │ ├── resizeTo.html │ │ ├── transformToFill.html │ │ └── transformToFit.html │ ├── Item │ │ ├── at.html │ │ ├── boundaries.html │ │ ├── createVector.html │ │ ├── getDistanceTo.html │ │ ├── getVectorTo.html │ │ ├── index.mjs │ │ ├── matchBasis.html │ │ ├── rotateBy.html │ │ ├── setBasis.html │ │ ├── setOrientation.html │ │ ├── setScale.html │ │ └── transformBy.html │ ├── Space │ │ ├── at.html │ │ ├── getBoundingBox.html │ │ └── index.mjs │ ├── Viewport │ │ ├── getAspectRatio.html │ │ ├── index.mjs │ │ └── measureGroup.html │ ├── default.css │ └── index.mjs ├── geometry │ ├── Area │ │ ├── index.mjs │ │ ├── projectTo.html │ │ └── transitRaw.html │ ├── Basis │ │ ├── at.html │ │ ├── changeBasis.html │ │ ├── createDirection.html │ │ ├── createVector.html │ │ ├── getMatchedOuter.html │ │ ├── getTransformTo.html │ │ ├── index.mjs │ │ ├── offsets.html │ │ ├── rotateBy.html │ │ ├── rotateByDegrees.html │ │ ├── scaleBy.html │ │ ├── transformBy.html │ │ ├── transitRaw.html │ │ ├── transitRawOuter.html │ │ └── translateBy.html │ ├── Box │ │ ├── almostEqual.html │ │ ├── at.html │ │ ├── atNorm.html │ │ ├── changeBasis.html │ │ ├── detectCollision.html │ │ ├── equal.html │ │ ├── fromBoxes.html │ │ ├── fromPoints.html │ │ ├── getArea.html │ │ ├── getBoundingBox.html │ │ ├── getBoundingCircle.html │ │ ├── getBoundingSphere.html │ │ ├── getInnerSquare.html │ │ ├── getVolume.html │ │ ├── index.mjs │ │ ├── normAt.html │ │ ├── projectTo.html │ │ ├── resizeTo.html │ │ ├── rotateBy.html │ │ ├── scaleBy.html │ │ ├── transitRaw.html │ │ └── translateBy.html │ ├── Circle │ │ ├── boundaries.html │ │ ├── collisions.html │ │ ├── construction.html │ │ ├── dimensions.html │ │ ├── equality.html │ │ ├── index.mjs │ │ ├── points.html │ │ ├── transformations.html │ │ ├── transitions.html │ │ └── translations.html │ ├── Direction │ │ ├── getRaw.html │ │ └── index.mjs │ ├── Distance │ │ ├── comparison.html │ │ ├── getRaw.html │ │ ├── index.mjs │ │ └── projection.html │ ├── Orientation │ │ ├── equality.html │ │ ├── getRaw.html │ │ └── index.mjs │ ├── Path │ │ ├── getRaw.html │ │ └── index.mjs │ ├── Point │ │ ├── addVector.html │ │ ├── getDistanceTo.html │ │ ├── getRaw.html │ │ ├── index.mjs │ │ ├── transformBy.html │ │ └── transitRawOuter.html │ ├── Ray │ │ ├── creation.html │ │ └── index.mjs │ ├── Scale │ │ ├── changeBasis.html │ │ └── index.mjs │ ├── Size │ │ ├── equality.html │ │ ├── getRaw.html │ │ ├── index.mjs │ │ └── scaleBy.html │ ├── Sphere │ │ ├── boundaries.html │ │ ├── collisions.html │ │ ├── construction.html │ │ ├── dimensions.html │ │ ├── equality.html │ │ ├── index.mjs │ │ ├── measures.html │ │ ├── points.html │ │ ├── transformations.html │ │ ├── transitions.html │ │ └── translations.html │ ├── Transform │ │ ├── getRaw.html │ │ └── index.mjs │ ├── Vector │ │ ├── fromPolar.html │ │ ├── fromSpherical.html │ │ ├── getRaw.html │ │ └── index.mjs │ ├── default.css │ └── index.mjs ├── run.mjs ├── suites.mjs ├── testlib.js ├── utils.mjs └── version │ ├── index.mjs │ └── version.html └── webpack.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,json,yml,css,html,md}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.github/workflows/tapspace-ci.yml: -------------------------------------------------------------------------------- 1 | name: Tapspace.js CI 2 | run-name: tapspace test run by ${{ github.actor }} 3 | 4 | on: [push] 5 | 6 | jobs: 7 | tapspace-headless: 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [20, 22] 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v4 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - run: npm install 21 | - run: npm test 22 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /.github 2 | 3 | /dist/.gitkeep 4 | /docs 5 | /test 6 | /features 7 | 8 | .editorconfig 9 | .gitignore 10 | 11 | .DS_Store 12 | package-lock.json 13 | -------------------------------------------------------------------------------- /dist/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/dist/.gitkeep -------------------------------------------------------------------------------- /docs/api/index.md: -------------------------------------------------------------------------------- 1 | # Tapspace API Documentation 2 | 3 | Choose version: 4 | 5 | - [Tapspace v1.6.0 API Docs](https://taataa.github.io/tapspace/api/v1/) 6 | - [Tapspace v2.0.0 API Docs](v2/) 7 | 8 | Or go [back to introduction](../) 9 | -------------------------------------------------------------------------------- /docs/api/v2/tapspace_class_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/api/v2/tapspace_class_chart.png -------------------------------------------------------------------------------- /docs/api/v2/tapspace_interaction_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/api/v2/tapspace_interaction_chart.png -------------------------------------------------------------------------------- /docs/collaborators/CoDHR_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/collaborators/CoDHR_logo.png -------------------------------------------------------------------------------- /docs/collaborators/lincs_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/collaborators/lincs_logo.png -------------------------------------------------------------------------------- /docs/collaborators/tampere_university_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/collaborators/tampere_university_logo.png -------------------------------------------------------------------------------- /docs/collaborators/texas_am_university_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/collaborators/texas_am_university_logo.png -------------------------------------------------------------------------------- /docs/collaborators/university_of_guelph_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/collaborators/university_of_guelph_logo.png -------------------------------------------------------------------------------- /docs/examples/assets/Ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/Ajax-loader.gif -------------------------------------------------------------------------------- /docs/examples/assets/arrowheads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/arrowheads.png -------------------------------------------------------------------------------- /docs/examples/assets/chellah_nw.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/chellah_nw.jpg -------------------------------------------------------------------------------- /docs/examples/assets/chellah_star.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/chellah_star.jpg -------------------------------------------------------------------------------- /docs/examples/assets/food-drink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/food-drink.png -------------------------------------------------------------------------------- /docs/examples/assets/gear11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/gear11.png -------------------------------------------------------------------------------- /docs/examples/assets/gear18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/gear18.png -------------------------------------------------------------------------------- /docs/examples/assets/gearhandle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/gearhandle.png -------------------------------------------------------------------------------- /docs/examples/assets/go-stone-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/go-stone-black.png -------------------------------------------------------------------------------- /docs/examples/assets/go-stone-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/go-stone-white.png -------------------------------------------------------------------------------- /docs/examples/assets/marrakech_knot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/marrakech_knot.jpg -------------------------------------------------------------------------------- /docs/examples/assets/marrakech_mosaic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/marrakech_mosaic.jpg -------------------------------------------------------------------------------- /docs/examples/assets/marrakech_sun.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/marrakech_sun.jpg -------------------------------------------------------------------------------- /docs/examples/assets/openmoji-cherry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/openmoji-cherry.png -------------------------------------------------------------------------------- /docs/examples/assets/oudaya_door.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/oudaya_door.jpg -------------------------------------------------------------------------------- /docs/examples/assets/rabat_sand.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/rabat_sand.jpg -------------------------------------------------------------------------------- /docs/examples/assets/square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/square.png -------------------------------------------------------------------------------- /docs/examples/assets/tile00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/tile00.png -------------------------------------------------------------------------------- /docs/examples/assets/tile01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/tile01.png -------------------------------------------------------------------------------- /docs/examples/assets/tile02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/tile02.png -------------------------------------------------------------------------------- /docs/examples/assets/tile03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/tile03.png -------------------------------------------------------------------------------- /docs/examples/assets/tile04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/tile04.png -------------------------------------------------------------------------------- /docs/examples/assets/tile05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/tile05.png -------------------------------------------------------------------------------- /docs/examples/assets/tile06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/tile06.png -------------------------------------------------------------------------------- /docs/examples/assets/tile07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/tile07.png -------------------------------------------------------------------------------- /docs/examples/assets/wood-cracked.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/assets/wood-cracked.jpg -------------------------------------------------------------------------------- /docs/examples/book/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/book/preview.png -------------------------------------------------------------------------------- /docs/examples/canvas/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/canvas/preview.jpg -------------------------------------------------------------------------------- /docs/examples/cielab/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/cielab/preview.png -------------------------------------------------------------------------------- /docs/examples/fruitfractal/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/fruitfractal/preview.jpg -------------------------------------------------------------------------------- /docs/examples/gears/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/gears/preview.jpg -------------------------------------------------------------------------------- /docs/examples/go/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/go/preview.jpg -------------------------------------------------------------------------------- /docs/examples/grid/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/grid/preview.jpg -------------------------------------------------------------------------------- /docs/examples/html/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/html/preview.jpg -------------------------------------------------------------------------------- /docs/examples/infinity/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/infinity/preview.jpg -------------------------------------------------------------------------------- /docs/examples/minimal/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/minimal/preview.png -------------------------------------------------------------------------------- /docs/examples/modes/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/modes/preview.jpg -------------------------------------------------------------------------------- /docs/examples/password/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/password/preview.jpg -------------------------------------------------------------------------------- /docs/examples/pixels/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/pixels/preview.jpg -------------------------------------------------------------------------------- /docs/examples/semanticzoom/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/semanticzoom/preview.jpg -------------------------------------------------------------------------------- /docs/examples/tiles/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/tiles/preview.jpg -------------------------------------------------------------------------------- /docs/examples/treeloader/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/treeloader/preview.png -------------------------------------------------------------------------------- /docs/examples/tunnel/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/tunnel/preview.jpg -------------------------------------------------------------------------------- /docs/examples/tutorial/index.html: -------------------------------------------------------------------------------- 1 | TODO the finished tutorial code 2 | -------------------------------------------------------------------------------- /docs/examples/visjs/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/examples/visjs/preview.png -------------------------------------------------------------------------------- /docs/taaspace-logo-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/taaspace-logo-128.png -------------------------------------------------------------------------------- /docs/taaspace-logo-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/taaspace-logo-256.png -------------------------------------------------------------------------------- /docs/taaspace-logo-6000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/taaspace-logo-6000.png -------------------------------------------------------------------------------- /docs/tapspace-banner-2018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/tapspace-banner-2018.png -------------------------------------------------------------------------------- /docs/tapspace-banner-2022.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/tapspace-banner-2022.png -------------------------------------------------------------------------------- /docs/tapspace-banner-2023.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/tapspace-banner-2023.png -------------------------------------------------------------------------------- /docs/tutorial/index.md: -------------------------------------------------------------------------------- 1 | # Tapspace.js Tutorials 2 | 3 | Select tutorial version: 4 | 5 | - [1.6.0](v1/) 6 | - [2.0.0-alpha](v2/) 7 | 8 | Or go [back to introduction](../) 9 | -------------------------------------------------------------------------------- /docs/tutorial/v2/coordinates_directions_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/docs/tutorial/v2/coordinates_directions_512.png -------------------------------------------------------------------------------- /features/assets/basis_2d_size800_unit500_orig100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/features/assets/basis_2d_size800_unit500_orig100.png -------------------------------------------------------------------------------- /features/assets/grid_size400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/features/assets/grid_size400.png -------------------------------------------------------------------------------- /features/assets/midjourney_creatures_cooking_question.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/features/assets/midjourney_creatures_cooking_question.jpg -------------------------------------------------------------------------------- /features/assets/midjourney_damp_giants_came_back_to_light.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/features/assets/midjourney_damp_giants_came_back_to_light.jpg -------------------------------------------------------------------------------- /features/assets/midjourney_steal_a_river_to_stare_some_woodland.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/features/assets/midjourney_steal_a_river_to_stare_some_woodland.jpg -------------------------------------------------------------------------------- /features/assets/midjourney_stretching_honor_guard_try_catch_wall_warriors_doom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/features/assets/midjourney_stretching_honor_guard_try_catch_wall_warriors_doom.jpg -------------------------------------------------------------------------------- /features/assets/midjourney_the_way_of_painful_cheese_and_thin_herbs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/features/assets/midjourney_the_way_of_painful_cheese_and_thin_herbs.jpg -------------------------------------------------------------------------------- /features/assets/placeholder400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/features/assets/placeholder400.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib') 2 | -------------------------------------------------------------------------------- /lib/capturers/Capturer/index.js: -------------------------------------------------------------------------------- 1 | const emitter = require('component-emitter') 2 | 3 | const Capturer = function () { 4 | // @Capturer() 5 | // 6 | // A base class for capturers. Every capturer must implement this interface. 7 | // Capturers may have extra methods on top of these. 8 | // 9 | 10 | // Inherit 11 | emitter(this) 12 | } 13 | 14 | module.exports = Capturer 15 | 16 | const proto = Capturer.prototype 17 | 18 | proto.bind = function () { 19 | throw new Error('Subclass must override this method.') 20 | } 21 | 22 | proto.update = function () { 23 | throw new Error('Subclass must override this method.') 24 | } 25 | 26 | proto.unbind = function () { 27 | throw new Error('Subclass must override this method.') 28 | } 29 | -------------------------------------------------------------------------------- /lib/capturers/GestureCapturer/Sensor/oncontextmenu.js: -------------------------------------------------------------------------------- 1 | module.exports = (sensor) => { 2 | return (ev) => { 3 | // Context menu was brought up somewhere in the document. 4 | // Therefore we want to cancel the ongoing gesture. 5 | 6 | // If no ongoing gesture, nothing to cancel. 7 | if (!sensor.started) { return } 8 | 9 | // Collect last pointers for emit. 10 | const lastPointers = Object.assign({}, sensor.currPointers) 11 | // Cancel all the pointers 12 | sensor.currPointers = {} 13 | 14 | // Store modifier keys 15 | sensor.registerModifiers(ev) 16 | 17 | // Declare gesture as cancelled 18 | sensor.started = false 19 | // TODO should we explicitly cancel also the pointer capture? 20 | sensor.oncancel(lastPointers, sensor.modifiers) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/capturers/GestureCapturer/Sensor/registerModifiers.js: -------------------------------------------------------------------------------- 1 | module.exports = function (ev) { 2 | // @Sensor:registerModifiers(ev) 3 | // 4 | // Update sensor modifier keyboard key (ctrl, alt, meta, shift) state 5 | // by the given pointer event. 6 | // The state is attached to the sent events. 7 | // Pointer events are MouseEvents and have altKey etc properties. 8 | // 9 | 10 | this.modifiers.ctrlKey = ev.ctrlKey 11 | this.modifiers.altKey = ev.altKey 12 | this.modifiers.metaKey = ev.metaKey 13 | this.modifiers.shiftKey = ev.shiftKey 14 | } 15 | -------------------------------------------------------------------------------- /lib/capturers/GestureCapturer/Sensor/unbind.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // Sensor:unbind() 3 | // 4 | // Remove all listeners we made. 5 | // 6 | 7 | this.element.removeEventListener('pointerdown', this.onpointerdown) 8 | this.element.removeEventListener('pointermove', this.onpointermove) 9 | this.element.removeEventListener('pointerup', this.onpointerup) 10 | this.element.removeEventListener('pointercancel', this.onpointercancel) 11 | this.onpointerdown = null 12 | this.onpointermove = null 13 | this.onpointerup = null 14 | this.onpointercancel = null 15 | 16 | window.removeEventListener('contextmenu', this.oncontextmenu) 17 | this.oncontextmenu = null 18 | } 19 | -------------------------------------------------------------------------------- /lib/capturers/GestureCapturer/Sensor/update.js: -------------------------------------------------------------------------------- 1 | module.exports = function (opts) { 2 | // @Sensor:update(opts) 3 | // 4 | // Update Sensor options on the fly. 5 | // 6 | // Parameters 7 | // opts 8 | // preventDefault, optional boolean 9 | // stopPropagation, optional boolean 10 | // 11 | this.options = Object.assign({ 12 | preventDefault: this.options.preventDefault, 13 | stopPropagation: this.options.stopPropagation 14 | }, opts) 15 | } 16 | -------------------------------------------------------------------------------- /lib/capturers/GestureCapturer/convertToActive.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const compose = fine.helm3.compose 3 | 4 | module.exports = (tr, origin) => { 5 | // Convert from passive to active transformation. 6 | // The passive has a location such as known origin position. 7 | // Remove the origin data to cancel any translation caused by 8 | // the scaling or rotation alone. 9 | // 10 | const x = origin.x 11 | const y = origin.y 12 | const pan = { 13 | a: 1, 14 | b: 0, 15 | x, 16 | y, 17 | z: 0 18 | } 19 | const ipan = { 20 | a: 1, 21 | b: 0, 22 | x: -x, 23 | y: -y, 24 | z: 0 25 | } 26 | return compose(ipan, compose(tr, pan)) 27 | } 28 | -------------------------------------------------------------------------------- /lib/capturers/GestureCapturer/getDeltaOrigin.js: -------------------------------------------------------------------------------- 1 | module.exports = (freedom, gestureMean) => { 2 | // Get the origin point for the transformation. 3 | // 4 | // Parameters: 5 | // freedom 6 | // a freedom object { type, pivot, angle } 7 | // gestureMean 8 | // a point2 9 | // 10 | // Return 11 | // a point2 { x, y } 12 | // 13 | if (freedom.pivot) { 14 | return freedom.pivot 15 | } 16 | // Else use the pointers center 17 | return gestureMean 18 | } 19 | -------------------------------------------------------------------------------- /lib/capturers/GestureCapturer/getPointersAverage.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const point2 = fine.point2 3 | 4 | module.exports = (pointers) => { 5 | // Get the mean point of the gesture. 6 | // 7 | // Parameters: 8 | // pointers 9 | // an object: id -> point2 10 | // 11 | // Return 12 | // a point2 { x, y } 13 | // 14 | return point2.mean(Object.values(pointers)) 15 | } 16 | -------------------------------------------------------------------------------- /lib/capturers/GestureCapturer/transitFreedom.js: -------------------------------------------------------------------------------- 1 | module.exports = (freedom, plane) => { 2 | // Normalise freedom parameter onto the root. 3 | // The result contains only raw geometry objects without basis. 4 | // 5 | const result = { 6 | type: freedom.type 7 | } 8 | 9 | // Normalise freedom pivot 10 | if (freedom.pivot) { 11 | result.pivot = plane.at(freedom.pivot).point 12 | } 13 | 14 | // TODO Normalise angle 15 | if (freedom.angle) { 16 | result.angle = freedom.angle 17 | } 18 | 19 | return result 20 | } 21 | -------------------------------------------------------------------------------- /lib/capturers/KeyboardCapturer/createKeydownHandler.js: -------------------------------------------------------------------------------- 1 | module.exports = (capturer) => { 2 | return function (ev) { 3 | // KeydownHandler 4 | // 5 | // Parameters: 6 | // ev 7 | // a KeyboardEvent, the browser keydown event. 8 | // 9 | if (ev.target === capturer.component.element) { 10 | // Re-emit 11 | capturer.emit('keydown', ev) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/capturers/KeyboardCapturer/createKeyupHandler.js: -------------------------------------------------------------------------------- 1 | module.exports = (capturer) => { 2 | return function (ev) { 3 | // KeyupHandler 4 | // 5 | // Parameters: 6 | // ev 7 | // a KeyboardEvent, the browser keyup event 8 | // 9 | if (ev.target === capturer.component.element) { 10 | // Re-emit 11 | capturer.emit('keyup', ev) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lib/capturers/index.js: -------------------------------------------------------------------------------- 1 | // @tapspace.capturers 2 | // 3 | // Classes to capture and preprocess input such as touch gestures 4 | // and mouse wheel movements. 5 | // 6 | 7 | // Short capturer names for Interactive:capturer 8 | exports.camera = require('./CameraCapturer') 9 | exports.gesture = require('./GestureCapturer') 10 | exports.keyboard = require('./KeyboardCapturer') 11 | exports.resize = require('./ResizeCapturer') 12 | exports.wheel = require('./WheelCapturer') 13 | -------------------------------------------------------------------------------- /lib/components/Animatable/cancelAnimation.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Animatable:cancelAnimation 3 | // 4 | // Cancel current animation. 5 | // 6 | // Return 7 | // this, for chaining 8 | // 9 | 10 | // TODO should emit idle or not? 11 | if (this.ontransitionend) { 12 | this.ontransitionend() 13 | } 14 | 15 | return this 16 | } 17 | -------------------------------------------------------------------------------- /lib/components/Animatable/index.js: -------------------------------------------------------------------------------- 1 | const Animatable = function () { 2 | // @Animatable 3 | // 4 | // Provides a Component animation abilities. 5 | // 6 | 7 | // To be able to stop CSS transitions and remove listeners gracefully, 8 | // we must remember possible handler functions. 9 | // See :animate and :animateOnce. 10 | this.ontransitioncancel = null 11 | this.ontransitionend = null 12 | } 13 | 14 | module.exports = Animatable 15 | const proto = Animatable.prototype 16 | proto.isAnimatable = true 17 | 18 | proto.animate = require('./animate') 19 | proto.animateOnce = require('./animateOnce') 20 | proto.cancelAnimation = require('./cancelAnimation') 21 | -------------------------------------------------------------------------------- /lib/components/Arc/create.js: -------------------------------------------------------------------------------- 1 | module.exports = (Arc) => { 2 | return (angle, border) => { 3 | // @Arc.create(angle, border) 4 | // tapspace.createArc 5 | // 6 | // Create an Arc item. Arcs are like edges but with a curved shape. 7 | // 8 | // Parameters: 9 | // angle 10 | // a number in degrees. The range is limited between 10 and 180 degrees. 11 | // border 12 | // a string, the border CSS style, for example '1px solid black'. 13 | // 14 | // Return 15 | // an Arc 16 | // 17 | return new Arc(angle, border) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/components/Arc/getLength.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../../geometry/Distance') 2 | 3 | module.exports = function () { 4 | // @Arc:getLength() 5 | // 6 | // Compute arc length. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | 12 | // Begin with radius 13 | const width = this.size.w 14 | const angle = this.angle 15 | const radius = width / (2 * Math.sin(angle / 2)) 16 | // Convert to circumference 17 | // const circum = 2 * Math.PI * radius 18 | // Take angle's share of the circumference 19 | // const arclen = circum * angle / (2 * Math.PI) 20 | // Simplify: 21 | const arclen = radius * angle 22 | 23 | return new Distance(this, arclen) 24 | } 25 | -------------------------------------------------------------------------------- /lib/components/Arc/getRadius.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../../geometry/Distance') 2 | 3 | module.exports = function () { 4 | // @Arc:getRadius() 5 | // 6 | // Get the arc radius. The smaller the sector angle, the bigger the radius. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | 12 | const width = this.size.w 13 | const angle = this.angle 14 | 15 | const radius = width / (2 * Math.sin(angle / 2)) 16 | 17 | return new Distance(this, radius) 18 | } 19 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/atBottomLeft.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:atBottomLeft() 3 | // 4 | // Get point at the bottom left corner of the element. 5 | // 6 | // Return 7 | // a Point 8 | // 9 | return this.atNorm(0, 1) 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/atBottomMid.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:atBottomMid() 3 | // 4 | // Get point at the middle of the bottom edge of the element. 5 | // 6 | // Return 7 | // a Point 8 | // 9 | return this.atNorm(0.5, 1) 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/atBottomRight.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:atBottomRight() 3 | // 4 | // Get point at the bottom right corner of the element. 5 | // 6 | // Return 7 | // a Point 8 | // 9 | return this.atNorm(1, 1) 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/atMidLeft.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:atMidLeft() 3 | // 4 | // Get point at the middle of the left edge of the element. 5 | // 6 | // Return 7 | // a Point 8 | // 9 | return this.atNorm(0, 0.5) 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/atMidMid.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:atMid() 3 | // @BlockComponent:atMidMid 4 | // @BlockComponent:atMiddle 5 | // @BlockComponent:atCenter 6 | // 7 | // Get point at the middle the element. 8 | // 9 | // Return 10 | // a Point 11 | // 12 | return this.atNorm(0.5, 0.5) 13 | } 14 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/atMidRight.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:atMidRight() 3 | // 4 | // Get point at the middle of the right edge of the element. 5 | // 6 | // Return 7 | // a Point 8 | // 9 | return this.atNorm(1, 0.5) 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/atNorm.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:atNorm(rx, ry) 3 | // @BlockComponent:getNormalizedPoint 4 | // 5 | // **Subclasses must override this method.** 6 | // 7 | // Get a point by coordinates that are normalized over the width and height 8 | // so that (0,0) means the top-left and (1,1) the bottom-right corner. 9 | // 10 | // Parameters: 11 | // rx 12 | // a number 13 | // ry 14 | // a number 15 | // 16 | // Return: 17 | // a Point 18 | // 19 | throw new Error('Subclass must override atNorm method.') 20 | } 21 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/atTopLeft.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:atTopLeft() 3 | // 4 | // Get point at the top left corner of the element. 5 | // 6 | // Return 7 | // a Point 8 | // 9 | return this.atNorm(0, 0) 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/atTopMid.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:atTopMid() 3 | // 4 | // Get point at the middle of the top edge of the element. 5 | // 6 | // Return 7 | // a Point 8 | // 9 | return this.atNorm(0.5, 0) 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/atTopRight.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:atTopRight() 3 | // 4 | // Get point at the top right corner of the element. 5 | // 6 | // Return 7 | // a Point 8 | // 9 | return this.atNorm(1, 0) 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/getArea.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:getArea() 3 | // 4 | // Get the block area. 5 | // 6 | // Return 7 | // an Area 8 | // 9 | return this.getSize().getArea() 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/getBoundingBox.js: -------------------------------------------------------------------------------- 1 | const Box = require('../../geometry/Box') 2 | 3 | module.exports = function () { 4 | // @BlockComponent:getBoundingBox() 5 | // 6 | // Get the bounding box of the block. 7 | // 8 | // Return: 9 | // a Box 10 | // 11 | const size = this.getSize().getRaw() 12 | const box = { a: 1, b: 0, x: 0, y: 0, z: 0, w: size.w, h: size.h, d: 0 } 13 | 14 | return new Box(this, box) 15 | } 16 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/getBoundingCircle.js: -------------------------------------------------------------------------------- 1 | const Circle = require('../../geometry/Circle') 2 | 3 | module.exports = function () { 4 | // @BlockComponent:getBoundingCircle() 5 | // 6 | // Get the bounding circle of the block. 7 | // 8 | // Return: 9 | // a Circle 10 | // 11 | const size = this.getSize().getRaw() 12 | const w = size.w / 2 13 | const h = size.h / 2 14 | const rad = Math.sqrt(w * w + h * h) 15 | 16 | const circle = { x: w, y: h, z: 0, r: rad } 17 | 18 | return new Circle(this, circle) 19 | } 20 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/getDiameter.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../../geometry/Distance') 2 | 3 | module.exports = function () { 4 | // @BlockComponent:getDiameter() 5 | // 6 | // Get the block diameter, from corner to opposite corner. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | const size = this.getSize().size 12 | const w = size.w 13 | const h = size.h 14 | return new Distance(this, Math.sqrt(w * w + h * h)) 15 | } 16 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/getHeight.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:getHeight() 3 | // 4 | // **Subclasses must override this method.** 5 | // 6 | // Get block height as a Distance. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | throw new Error('Subclass must override getHeight method.') 12 | } 13 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/getInnerSquare.js: -------------------------------------------------------------------------------- 1 | const Box = require('../../geometry/Box') 2 | 3 | module.exports = function () { 4 | // @BlockComponent:getInnerSquare() 5 | // 6 | // Get the largest square box inside the block that has the same center. 7 | // 8 | // Return: 9 | // a Box 10 | // 11 | const size = this.getSize().getRaw() 12 | const bw = size.w 13 | const bh = size.h 14 | const offset = Math.abs((bw - bh) / 2) 15 | 16 | let box 17 | if (bw > bh) { 18 | // width longer, height limits 19 | box = { a: 1, b: 0, x: offset, y: 0, z: 0, w: bh, h: bh, d: 0 } 20 | } else { 21 | // height longer, width limits 22 | box = { a: 1, b: 0, x: 0, y: offset, z: 0, w: bw, h: bw, d: 0 } 23 | } 24 | 25 | return new Box(this, box) 26 | } 27 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/getSize.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:getSize() 3 | // 4 | // **Subclasses must override this method.** 5 | // 6 | // Get block size dimensions. 7 | // 8 | // Return 9 | // a Size 10 | // 11 | throw new Error('Subclass must override getSize method.') 12 | } 13 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/getWidth.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @BlockComponent:getWidth() 3 | // 4 | // **Subclasses must override this method.** 5 | // 6 | // Get block width as a Distance. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | throw new Error('Subclass must override getWidth method.') 12 | } 13 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/moveCenterTo.js: -------------------------------------------------------------------------------- 1 | module.exports = function (position) { 2 | // @BlockComponent:moveCenterTo(position) 3 | // 4 | // Translate the block so that its middle point matches the given point. 5 | // 6 | // Parameters: 7 | // position 8 | // a Point, or a point2 { x, y } on the parent basis. 9 | // ..The block will be moved on the parent so that 10 | // ..the center of the block matches the position. 11 | // 12 | // Return 13 | // this, for chaining 14 | // 15 | 16 | this.match({ 17 | source: this.atMid(), 18 | target: position 19 | }) 20 | 21 | return this 22 | } 23 | -------------------------------------------------------------------------------- /lib/components/BlockComponent/normAt.js: -------------------------------------------------------------------------------- 1 | module.exports = function (x, y) { 2 | // @BlockComponent:normAt(x, y) 3 | // 4 | // **Subclasses must override this method.** 5 | // 6 | // Get normalized coordinates from a point. 7 | // Practically this is the inverse of BlockComponent:atNorm. 8 | // For example, let a block have size (4, 4). Then the normalized coords 9 | // for the point (2, 1) are (0.5, 0.25). 10 | // 11 | // Parameters: 12 | // x 13 | // a number 14 | // y 15 | // a number 16 | // 17 | // Alternative parameters: 18 | // point 19 | // a Point 20 | // 21 | // Return 22 | // a { rx, ry }. If size is zero, will return { rx: 0, ry: 0 }. 23 | // 24 | throw new Error('Subclass must override normAt method.') 25 | } 26 | -------------------------------------------------------------------------------- /lib/components/Component/addClass.js: -------------------------------------------------------------------------------- 1 | module.exports = function (className) { 2 | // @Component:addClass(className[, secondClass[, ...]]) 3 | // 4 | // Add one or more CSS class name into the affine element, up to three. 5 | // This is equivalent to `basis.element.classList.add(className)`. 6 | // 7 | // Example: 8 | // `item.addClass('myitem', 'banner')` 9 | // 10 | // Parameters: 11 | // className 12 | // a string, for example 'my-item' 13 | // 14 | // Return 15 | // this, for chaining 16 | // 17 | // Complexity: 18 | // O(1) 19 | // 20 | 21 | const list = this.element.classList 22 | list.add.apply(list, arguments) 23 | 24 | return this 25 | } 26 | -------------------------------------------------------------------------------- /lib/components/Component/addLink.js: -------------------------------------------------------------------------------- 1 | module.exports = function (key, target) { 2 | // @Component:addLink(key, target) 3 | // 4 | // Parameters: 5 | // key 6 | // a string 7 | // target 8 | // a Component 9 | // 10 | 11 | if (typeof key !== 'string') { 12 | throw new Error('Invalid key') 13 | } 14 | 15 | if (!target.isComponent) { 16 | throw new Error('Invalid target') 17 | } 18 | 19 | this.links[key] = target 20 | 21 | return this 22 | } 23 | -------------------------------------------------------------------------------- /lib/components/Component/atAnchor.js: -------------------------------------------------------------------------------- 1 | const Point = require('../../geometry/Point') 2 | 3 | module.exports = function () { 4 | // @Component:atAnchor() 5 | // 6 | // Get origin point. At zero by default. 7 | // 8 | // Return 9 | // a Point 10 | // 11 | 12 | return new Point(this, { x: 0, y: 0, z: 0 }) 13 | } 14 | -------------------------------------------------------------------------------- /lib/components/Component/bringAbove.js: -------------------------------------------------------------------------------- 1 | module.exports = function (target) { 2 | // @Component:bringAbove(target) 3 | // 4 | // Reinsert the element above the given target element. 5 | // 6 | // This method reorders elements in DOM. That can cause elements to lose 7 | // their hover state in some browsers. See issue #173 for details. 8 | // 9 | // Parameters: 10 | // target 11 | // a Component 12 | // 13 | // Return 14 | // this, for chaining 15 | // 16 | 17 | // Reinsert only when necessary to prevent flicker from rendering. 18 | if (this.element.nextElementSibling !== target.element) { 19 | const nextSibling = target.element.nextElementSibling 20 | this.element.parentNode.insertBefore(this.element, nextSibling) 21 | } 22 | 23 | return this 24 | } 25 | -------------------------------------------------------------------------------- /lib/components/Component/bringToFront.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:bringToFront() 3 | // 4 | // Remove this element and reinsert it as the last child. 5 | // 6 | // This method reorders elements in DOM. That can cause elements to lose 7 | // their hover state in some browsers. See issue #173 for details. 8 | // 9 | // Return 10 | // this, for chaining 11 | // 12 | 13 | const parentNode = this.element.parentNode 14 | 15 | // Reinsert only when necessary to prevent flicker from rendering. 16 | if (parentNode.lastChild !== this.element) { 17 | parentNode.appendChild(this.element) 18 | } 19 | 20 | return this 21 | } 22 | -------------------------------------------------------------------------------- /lib/components/Component/createDistance.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../../geometry/Distance') 2 | 3 | module.exports = function (dist) { 4 | // @Component:createDistance(dist) 5 | // 6 | // Create a Distance relative to the component. 7 | // 8 | // Parameters 9 | // dist 10 | // a number, a distance in the coordinate space of the component. 11 | // 12 | // Return 13 | // a Distance 14 | // 15 | 16 | if (typeof dist !== 'number') { 17 | throw new Error('Invalid distance. Must be a number.') 18 | } 19 | 20 | return new Distance(this, dist) 21 | } 22 | -------------------------------------------------------------------------------- /lib/components/Component/createVector.js: -------------------------------------------------------------------------------- 1 | const Vector = require('../../geometry/Vector') 2 | 3 | module.exports = function (x, y, z) { 4 | // @Component:createVector(x, y[, z]) 5 | // 6 | // Create a Vector on this basis. 7 | // 8 | // Parameters 9 | // x 10 | // a number 11 | // y 12 | // a number 13 | // z 14 | // optional number, default to zero. 15 | // 16 | // Return 17 | // a Vector 18 | // 19 | 20 | // Default 21 | z = z || 0 22 | 23 | return new Vector(this, { x, y, z }) 24 | } 25 | -------------------------------------------------------------------------------- /lib/components/Component/dom/isAffine.js: -------------------------------------------------------------------------------- 1 | module.exports = (element) => { 2 | // @Component.isAffine(element) 3 | // 4 | // Test if the given HTMLElement is affine. 5 | // An HTMLElement is affine if elem.affine object is set 6 | // and that the elem.affine is a Component or inherits Component. 7 | // 8 | // Example: 9 | // ``` 10 | // const el = document.getElementById('myelem') 11 | // if (Component.isAffine(el)) { ... } 12 | // ``` 13 | // 14 | // Parameters: 15 | // element 16 | // an HTMLElement or any other object. 17 | // 18 | // Return: 19 | // boolean. True if the given element is HTMLElement with affine property. 20 | // 21 | // Complexity: 22 | // O(1) 23 | // 24 | return element && element.affine 25 | } 26 | -------------------------------------------------------------------------------- /lib/components/Component/followLink.js: -------------------------------------------------------------------------------- 1 | module.exports = function (key) { 2 | // @Component:followLink(key) 3 | // 4 | // Get link target. 5 | // 6 | // Parameters: 7 | // key 8 | // a string 9 | // 10 | // Return 11 | // a Component or null 12 | // 13 | 14 | const target = this.links[key] 15 | 16 | if (target) { 17 | return target 18 | } 19 | 20 | return null 21 | } 22 | -------------------------------------------------------------------------------- /lib/components/Component/getAncestors.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:getAncestors() 3 | // 4 | // Get an array of affine ancestors of this node, ordered from 5 | // the immediate parent to the farthest ancestor, the immediate parent first. 6 | // The list of ancestors includes a space and a viewport, given that 7 | // the node is placed in a space. 8 | // 9 | // Return 10 | // array of Component 11 | // 12 | // Complexity 13 | // O(d) where d is the depth of the affine tree 14 | // 15 | const arr = [] 16 | let par = this.element.parentElement 17 | while (par && par.affine) { 18 | arr.push(par.affine) 19 | par = par.parentElement 20 | } 21 | return arr 22 | } 23 | -------------------------------------------------------------------------------- /lib/components/Component/getBasis.js: -------------------------------------------------------------------------------- 1 | const Basis = require('../../geometry/Basis') 2 | 3 | module.exports = function () { 4 | // @Component:getBasis() 5 | // 6 | // Get the virtual basis of this component. 7 | // Provides a way to construct new bases via transformations without 8 | // transforming the component itself. Can also be used to match bases 9 | // between components. 10 | // 11 | // Example: 12 | // ``` 13 | // > const basis = item.getBasis().rotateByDegrees(45) 14 | // > anotherItem.setBasis(basis) 15 | // ``` 16 | // 17 | // Return 18 | // a Basis 19 | // 20 | return new Basis(this, { a: 1, b: 0, x: 0, y: 0, z: 0 }) 21 | } 22 | -------------------------------------------------------------------------------- /lib/components/Component/getBasisAt.js: -------------------------------------------------------------------------------- 1 | const Basis = require('../../geometry/Basis') 2 | 3 | module.exports = function (origin) { 4 | // @Component:getBasisAt(origin) 5 | // 6 | // Get a virtual basis on this component. 7 | // The basis has same scale and orientation as the viewport but 8 | // matches the given origin point. 9 | // 10 | // Example: 11 | // ``` 12 | // > const basis = item.getBasisAt(item.atBottomLeft()) 13 | // > anotherItem.setBasis(basis) 14 | // ``` 15 | // 16 | // Parameters: 17 | // origin 18 | // a Point 19 | // 20 | // Return 21 | // a Basis 22 | // 23 | if (origin.transitRaw) { 24 | origin = origin.transitRaw(this) 25 | } 26 | return new Basis(this, { 27 | a: 1, 28 | b: 0, 29 | x: origin.x, 30 | y: origin.y, 31 | z: origin.z 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /lib/components/Component/getChildren.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:getChildren() 3 | // 4 | // Get all affine child nodes of this node. The children are found via DOM. 5 | // The children in DOM that do not have affine properties will be skipped. 6 | // 7 | // Return 8 | // array of Component 9 | // 10 | // Complexity 11 | // O(c) where c is the number of children 12 | // 13 | const el = this.element 14 | const result = [] 15 | for (let i = 0; i < el.children.length; i += 1) { 16 | if (el.children[i].affine) { 17 | result.push(el.children[i].affine) 18 | } 19 | } 20 | return result 21 | } 22 | -------------------------------------------------------------------------------- /lib/components/Component/getDescendants.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:getDescendants() 3 | // 4 | // All affine descendants in a list, including the children of this node. 5 | // The affine descendants must be connected in affine part of DOM. 6 | // 7 | // Return 8 | // array of Component 9 | // 10 | // Complexity: 11 | // O(n) where n is the number of nodes in the affine tree 12 | // 13 | let arr = [] 14 | const children = this.getChildren() 15 | for (let i = 0; i < children.length; i += 1) { 16 | const child = children[i] 17 | arr = arr.concat(child, child.getDescendants()) 18 | } 19 | return arr 20 | } 21 | -------------------------------------------------------------------------------- /lib/components/Component/getElement.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:getElement() 3 | // 4 | // Get the affine HTML element of the node. 5 | // Each Component has one HTML element. 6 | // This element can wrap further affine or non-affine content. 7 | // 8 | // Return 9 | // an HTMLElement 10 | // 11 | // Complexity: 12 | // O(1) 13 | // 14 | return this.element 15 | } 16 | -------------------------------------------------------------------------------- /lib/components/Component/getLeaves.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:getLeaves() 3 | // 4 | // All affine leaf descendants in a list. A leaf has no own children. 5 | // The affine leaves must be connected to this node in the subset of DOM. 6 | // An affine leaf may have non-affine children in DOM. 7 | // 8 | // Return 9 | // array of Component 10 | // 11 | // Complexity: 12 | // O(n) where n is the number of nodes in the affine tree. 13 | // 14 | const offspring = this.getDescendants() 15 | return offspring.filter((node) => { 16 | return node.isLeaf() 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /lib/components/Component/getOrientation.js: -------------------------------------------------------------------------------- 1 | const Orientation = require('../../geometry/Orientation') 2 | 3 | module.exports = function () { 4 | // @Component:getOrientation() 5 | // 6 | // Get the orientation of this component. 7 | // Provides a way to match the orientation between components. 8 | // 9 | // Return 10 | // an Orientation 11 | // 12 | return new Orientation(this, { a: 1, b: 0 }) 13 | } 14 | -------------------------------------------------------------------------------- /lib/components/Component/getParent.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:getParent() 3 | // 4 | // Get the affine parent of the plane. Null if no affine parent. 5 | // 6 | // Return 7 | // a Component, the parent. 8 | // null if no affine parent. 9 | // 10 | // Complexity: 11 | // O(1) 12 | // 13 | const domParent = this.element.parentElement 14 | if (domParent && domParent.affine) { 15 | return domParent.affine 16 | } 17 | return null 18 | } 19 | -------------------------------------------------------------------------------- /lib/components/Component/getRoot.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:getRoot() 3 | // 4 | // Get the affine root. Will return self if has no affine parent. 5 | // 6 | // Return 7 | // a Component 8 | // 9 | // Complexity 10 | // O(d) where d is the depth of the affine tree. 11 | // 12 | 13 | let root = this 14 | let par = this.element.parentElement 15 | while (par && par.affine) { 16 | root = par.affine 17 | par = par.parentElement 18 | } 19 | 20 | return root 21 | } 22 | -------------------------------------------------------------------------------- /lib/components/Component/getScale.js: -------------------------------------------------------------------------------- 1 | const Scale = require('../../geometry/Scale') 2 | 3 | module.exports = function () { 4 | // @Component:getScale() 5 | // 6 | // The scale of the basis. 7 | // 8 | // Return 9 | // a Scale 10 | // 11 | return new Scale(this, 1) 12 | } 13 | -------------------------------------------------------------------------------- /lib/components/Component/getTransitionToParent.js: -------------------------------------------------------------------------------- 1 | const plane3 = require('affineplane').plane3 2 | 3 | module.exports = function () { 4 | // @Component:getTransitionToParent() 5 | // 6 | // Get a coordinate transition matrix from this basis to its parent basis. 7 | // If this basis does not have a parent, it is either root affine node in DOM 8 | // or is not yet added to DOM, and in this case a transition matrix to 9 | // a virtual parent is returned. 10 | // 11 | // Return 12 | // a plane3, the coordinate transition matrix from this to parent. 13 | // 14 | // Complexity 15 | // O(1) 16 | // 17 | return plane3.copy(this.tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/components/Component/getViewport.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:getViewport() 3 | // 4 | // Get the affine viewport this basis currently belongs to, if any. 5 | // Will return null if the basis is not connected to a viewport. 6 | // Useful for checking if the component is rendered. 7 | // 8 | // Return 9 | // a Viewport 10 | // 11 | // Complexity 12 | // O(d) where d is the depth of the affine tree. 13 | // 14 | 15 | const candidate = this.getRoot() 16 | 17 | if (candidate.isViewport) { 18 | return candidate 19 | } 20 | 21 | return null 22 | } 23 | -------------------------------------------------------------------------------- /lib/components/Component/hasClass.js: -------------------------------------------------------------------------------- 1 | module.exports = function (className) { 2 | // @Component:hasClass(className) 3 | // 4 | // Test if the element has the given class name. 5 | // This is equivalent to `basis.element.classList.contains(className)`. 6 | // 7 | // Example: 8 | // `if (item.hasClass('myitem')) { ... }` 9 | // 10 | // Parameters: 11 | // className 12 | // a string, for example 'banner' 13 | // 14 | // Return 15 | // a boolean 16 | // 17 | return this.element.classList.contains(className) 18 | } 19 | -------------------------------------------------------------------------------- /lib/components/Component/hasLink.js: -------------------------------------------------------------------------------- 1 | module.exports = function (key) { 2 | // @Component:hasLink(key) 3 | // 4 | // Parameters: 5 | // key 6 | // a string 7 | // 8 | // Return 9 | // a boolean 10 | // 11 | return key in this.links 12 | } 13 | -------------------------------------------------------------------------------- /lib/components/Component/isLeaf.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:isLeaf() 3 | // 4 | // This basis is a leaf if it has no affine children in DOM. 5 | // A leaf can have non-affine children in DOM. 6 | // A basis that is not a leaf has one or more affine children and 7 | // may also have non-affine chilren. 8 | // 9 | // Return 10 | // a boolean, true if the basis has no affine children. 11 | // 12 | // Complexity 13 | // O(c) where c is the number of children. 14 | // 15 | return this.getChildren().length === 0 16 | } 17 | -------------------------------------------------------------------------------- /lib/components/Component/isRoot.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:isRoot() 3 | // 4 | // Test is the basis an affine root i.e. 5 | // does the element have no affine parent. 6 | // A root basis can have non-affine parent in DOM. 7 | // 8 | // Return 9 | // a boolean 10 | // 11 | // Complexity 12 | // O(1) 13 | // 14 | const parent = this.element.parentElement 15 | if (parent) { 16 | if (parent.affine) { 17 | // has affine parent 18 | return false 19 | } 20 | // has parent but it is not affine 21 | return true 22 | } 23 | // is DOM root, so yes. 24 | return true 25 | } 26 | -------------------------------------------------------------------------------- /lib/components/Component/remove.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:remove() 3 | // 4 | // Remove this component from its parent, if any. 5 | // If you also need to remove active listeners, call `component.off()`. 6 | // 7 | // Return 8 | // this, for chaining 9 | // 10 | 11 | // Detach from document 12 | const elem = this.element 13 | const parent = elem.parentElement 14 | 15 | if (parent) { 16 | parent.removeChild(elem) 17 | } 18 | 19 | return this 20 | } 21 | -------------------------------------------------------------------------------- /lib/components/Component/removeClass.js: -------------------------------------------------------------------------------- 1 | module.exports = function (className) { 2 | // @Component:removeClass 3 | // 4 | // Remove a CSS class name from the affine element. 5 | // This is equivalent to `basis.element.classList.remove(className)`. 6 | // 7 | // Parameters: 8 | // className 9 | // a string, for example 'my-item' 10 | // 11 | // Return 12 | // this, for chaining 13 | // 14 | // Complexity 15 | // O(1) 16 | // 17 | this.element.classList.remove(className) 18 | 19 | return this 20 | } 21 | -------------------------------------------------------------------------------- /lib/components/Component/removeLink.js: -------------------------------------------------------------------------------- 1 | module.exports = function (key, target) { 2 | // @Component:removeLink(key, target) 3 | // 4 | // Parameters: 5 | // key 6 | // a string 7 | // target 8 | // a Component 9 | // 10 | 11 | if (typeof key !== 'string') { 12 | throw new Error('Invalid key') 13 | } 14 | 15 | if (!target) { 16 | delete this.links[key] 17 | } 18 | 19 | if (target.isComponent) { 20 | if (this.links[key] === target) { 21 | delete this.links[key] 22 | } 23 | } 24 | 25 | return this 26 | } 27 | -------------------------------------------------------------------------------- /lib/components/Component/removeLinks.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:removeLinks() 3 | // 4 | // Remove all links. 5 | // 6 | // Return 7 | // this, for chaining 8 | // 9 | 10 | // Remove all instances from adjacent. 11 | Object.keys(this.links).forEach(key => { 12 | const neighbor = this.links[key] 13 | 14 | Object.keys(neighbor.links).forEach(keyy => { 15 | neighbor.removeLink(keyy, this) 16 | }) 17 | 18 | delete this.links[key] 19 | }) 20 | 21 | this.links = {} 22 | 23 | return this 24 | } 25 | -------------------------------------------------------------------------------- /lib/components/Component/requestIdle.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:requestIdle() 3 | // 4 | // Targeted for internal use. 5 | // 6 | // Ask component to emit an idle event. 7 | // The idle events are used for computationally heavy tasks. 8 | // Therefore this method attempts to limit the frequency of idle events 9 | // by throttling and respecting ongoing animations. 10 | // 11 | // Return 12 | // this, for chaining 13 | // 14 | 15 | if (this.idleTimeout) { 16 | clearTimeout(this.idleTimeout) 17 | } 18 | 19 | this.idleTimeout = setTimeout(() => { 20 | if (!this.ontransitionend) { 21 | this.emit('idle') 22 | } 23 | // else idle will be requested later in ontransitionend 24 | }, 150) 25 | 26 | return this 27 | } 28 | -------------------------------------------------------------------------------- /lib/components/Component/sendToBack.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Component:sendToBack() 3 | // 4 | // Remove this element and reinsert it as the first child. 5 | // The element will be rendered first and thus becomes the farthest and 6 | // and bottommost. 7 | // 8 | // This method reorders elements in DOM. That can cause elements to lose 9 | // their hover state in some browsers. See issue #173 for details. 10 | // 11 | // Return 12 | // this, for chaining 13 | // 14 | 15 | const parentNode = this.element.parentNode 16 | 17 | // Reinsert only when necessary to prevent flicker from rendering. 18 | if (parentNode.firstChild !== this.element) { 19 | parentNode.prepend(this.element) 20 | } 21 | 22 | return this 23 | } 24 | -------------------------------------------------------------------------------- /lib/components/Component/setId.js: -------------------------------------------------------------------------------- 1 | module.exports = function (elemId) { 2 | // @Component:setId 3 | // 4 | // Set the affine element ID property. 5 | // This is equivalent to `basis.element.id = elemId`. 6 | // 7 | // Parameters: 8 | // elemId 9 | // a string, for example 'hero' 10 | // 11 | // Return 12 | // this, for chaining 13 | // 14 | // Complexity: 15 | // O(1) 16 | // 17 | this.element.id = elemId 18 | 19 | return this 20 | } 21 | -------------------------------------------------------------------------------- /lib/components/Composite/getSize.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Composite:getSize() 3 | // 4 | // Get the size of the bounding box of the component, 5 | // including the children and their descendants. 6 | // Can be computationally heavy if there is lots of descendants. 7 | // 8 | // Return 9 | // a Size 10 | // 11 | const bounds = this.getBoundingBox() 12 | return bounds.getSize() 13 | } 14 | -------------------------------------------------------------------------------- /lib/components/CustomControl/create.js: -------------------------------------------------------------------------------- 1 | module.exports = (CustomControl) => { 2 | return (content) => { 3 | // @tapspace.createControl(content) 4 | // @CustomControl.create 5 | // 6 | // Create a viewport control with custom HTML content. 7 | // Useful for logos, search, and legal text. 8 | // 9 | // Parameters: 10 | // content 11 | // an HTMLElement or HTML string. The given element(s) will be 12 | // ..wrapped in a div. 13 | // 14 | // Return 15 | // a CustomControl 16 | // 17 | return new CustomControl(content) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/components/Edge/atEnd.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Edge:atEnd() 3 | // 4 | // Get the Point at the edge ending, at the middle of the border. 5 | // 6 | // Return: a Point 7 | // 8 | return this.at( 9 | this.endpoint.x, 10 | this.width / 2 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /lib/components/Edge/atStart.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Edge:atStart() 3 | // 4 | // Get the Point at the edge beginning, at the middle of the border. 5 | // 6 | // Return: a Point 7 | // 8 | return this.at(0, this.width / 2) 9 | } 10 | -------------------------------------------------------------------------------- /lib/components/Edge/create.js: -------------------------------------------------------------------------------- 1 | module.exports = (Edge) => { 2 | return (width) => { 3 | // tapspace.createEdge([width]) 4 | // Edge.create 5 | // 6 | // Create an edge component. The edge is a straight line between 7 | // two points. Remember to set endpoints after adding the edge to space. 8 | // 9 | // Parameters: see Edge 10 | // 11 | // Return 12 | // an Edge 13 | // 14 | return new Edge(width) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/components/Edge/getBoundingBox.js: -------------------------------------------------------------------------------- 1 | const Box = require('../../geometry/Box') 2 | 3 | module.exports = function () { 4 | // @Edge:getBoundingBox() 5 | // 6 | // Get the bounding box of the edge. 7 | // 8 | // Return: 9 | // a Box 10 | // 11 | 12 | // Remember, the edge is always horizontal in its local basis. 13 | const box = { 14 | a: 1, 15 | b: 0, 16 | // Position. Match element top left corner. 17 | x: 0, 18 | y: 0, 19 | // The endpoint can be in the front or back. 20 | // If in the front, we must bring the box forward. 21 | z: Math.min(this.startpoint.z, this.endpoint.z), 22 | // Sizes. 23 | w: Math.abs(this.endpoint.x - this.startpoint.x), 24 | h: this.width, 25 | d: Math.abs(this.endpoint.z - this.startpoint.z) 26 | } 27 | 28 | return new Box(this, box) 29 | } 30 | -------------------------------------------------------------------------------- /lib/components/Edge/getLength.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Edge:getLength() 3 | // 4 | // Get length of the edge. 5 | // 6 | // Return 7 | // a Distance 8 | // 9 | const p0 = this.at(this.startpoint) 10 | const p1 = this.at(this.endpoint) 11 | const dist = p0.getDistanceTo(p1) 12 | return dist 13 | } 14 | -------------------------------------------------------------------------------- /lib/components/Edge/renderTransform.js: -------------------------------------------------------------------------------- 1 | const applyTransform2d = require('../Transformer/dom/applyTransform2d') 2 | 3 | module.exports = function () { 4 | // @Edge:renderTransform() 5 | // 6 | // Refresh the edge orientation. 7 | // 8 | 9 | // Planar 2D 10 | applyTransform2d(this.element, this.tran) 11 | } 12 | -------------------------------------------------------------------------------- /lib/components/Edge/setWidth.js: -------------------------------------------------------------------------------- 1 | module.exports = function (width) { 2 | // @Edge:setWidth(width) 3 | // 4 | // Change edge pixel width. 5 | // 6 | // Parameters: 7 | // width 8 | // an integer 9 | // 10 | // Return 11 | // this, for chaining 12 | // 13 | 14 | if (typeof width !== 'number') { 15 | throw new Error('Invalid edge width: ' + width) 16 | } 17 | 18 | // Points before changing width. 19 | const start = this.atStart() 20 | const end = this.atEnd() 21 | 22 | this.width = width 23 | 24 | // Reset points 25 | this.setPoints(start, end) 26 | 27 | return this 28 | } 29 | -------------------------------------------------------------------------------- /lib/components/FrameComponent/getHeight.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../../geometry/Distance') 2 | 3 | module.exports = function () { 4 | // @FrameComponent:getHeight() 5 | // 6 | // Get frame height as a Distance. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | return new Distance(this, this.size.h) 12 | } 13 | -------------------------------------------------------------------------------- /lib/components/FrameComponent/getSize.js: -------------------------------------------------------------------------------- 1 | const Size = require('../../geometry/Size') 2 | 3 | module.exports = function () { 4 | // @FrameComponent:getSize() 5 | // 6 | // Get frame size dimensions in pixels. 7 | // 8 | // Return 9 | // a Size 10 | // 11 | return new Size(this, { 12 | w: this.size.w, 13 | h: this.size.h, 14 | d: 0 15 | }) 16 | } 17 | -------------------------------------------------------------------------------- /lib/components/FrameComponent/getWidth.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../../geometry/Distance') 2 | 3 | module.exports = function () { 4 | // @FrameComponent:getWidth() 5 | // 6 | // Get frame width as a Distance. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | return new Distance(this, this.size.w) 12 | } 13 | -------------------------------------------------------------------------------- /lib/components/FrameComponent/matchPixelSize.js: -------------------------------------------------------------------------------- 1 | module.exports = function (target) { 2 | // @FrameComponent:matchPixelSize(target) 3 | // 4 | // Resize this frame so that its pixel size matches the pixel size 5 | // of the target regardless of their scale. If you need to match their 6 | // physical sizes after scale, use FrameComponent:matchSize. 7 | // 8 | // Parameters 9 | // target 10 | // a FrameComponent 11 | // 12 | // Return 13 | // this, for chaining 14 | // 15 | 16 | // The question is: how much to add to the inner size here so that 17 | // the physical size equals the target. 18 | 19 | const ts = target.getSize().size 20 | this.setSize(ts) 21 | 22 | return this 23 | } 24 | -------------------------------------------------------------------------------- /lib/components/FrameComponent/matchSize.js: -------------------------------------------------------------------------------- 1 | module.exports = function (target) { 2 | // @FrameComponent:matchSize(target) 3 | // 4 | // Resize this frame so that its physical size matches the physical size 5 | // of the target. The physical size is the size after scaling. 6 | // If you need to match the inner width and height in pixels regardless of 7 | // scale, use FrameComponent:matchPixelSize. 8 | // 9 | // Parameters 10 | // target 11 | // a FrameComponent 12 | // 13 | // Return 14 | // this, for chaining 15 | // 16 | 17 | // The question is: how much to add to the inner size here so that 18 | // the physical size equals the target. 19 | 20 | const targetSize = target.getSize() 21 | this.setSize(targetSize) 22 | 23 | return this 24 | } 25 | -------------------------------------------------------------------------------- /lib/components/FrameComponent/setHeight.js: -------------------------------------------------------------------------------- 1 | module.exports = function (height, pivot) { 2 | // @FrameComponent:setHeight(height[, pivot]) 3 | // 4 | // Resize this frame to the given height. 5 | // Keep width intact thus do not preserve the aspect ratio. 6 | // The resize is performed about a fixed pivot point. 7 | // 8 | // Parameters: 9 | // height 10 | // a Distance, the new height as a distance in space. 11 | // pivot 12 | // optional Point, defaults to the transform origin (=anchor) 13 | // 14 | // Return 15 | // this, for chaining 16 | // 17 | 18 | // Normalize 19 | if (height.transitRaw) { 20 | height = height.transitRaw(this) 21 | } 22 | 23 | this.resizeTo({ w: this.size.w, h: height }, pivot) 24 | 25 | return this 26 | } 27 | -------------------------------------------------------------------------------- /lib/components/FrameComponent/setWidth.js: -------------------------------------------------------------------------------- 1 | module.exports = function (width, pivot) { 2 | // @FrameComponent:setWidth(width[, pivot]) 3 | // 4 | // Resize this frame to the given width. 5 | // Keep height intact thus do not preserve the aspect ratio. 6 | // The resize is performed about a fixed pivot point. 7 | // 8 | // Parameters: 9 | // width 10 | // a Distance, the new width as a distance in space. 11 | // pivot 12 | // optional Point, defaults to the transform origin (=anchor) 13 | // 14 | // Return 15 | // this, for chaining 16 | // 17 | 18 | // Normalize 19 | if (width.transitRaw) { 20 | width = width.transitRaw(this) 21 | } 22 | 23 | this.resizeTo({ w: width, h: this.size.h }, pivot) 24 | 25 | return this 26 | } 27 | -------------------------------------------------------------------------------- /lib/components/Hyperspace/atAnchor.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Hyperspace:atAnchor() 3 | // 4 | // Reuse viewport anchor. 5 | // 6 | return this.viewport.atAnchor() 7 | } 8 | -------------------------------------------------------------------------------- /lib/components/Hyperspace/requestIdle.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // Override Component:requestIdle 3 | // 4 | // Same as the normal requestIdle but targets the viewport. 5 | // Hyperspace is not interactive, thus it does not track idle status 6 | // unlike viewport. 7 | 8 | this.viewport.requestIdle() 9 | 10 | return this 11 | } 12 | -------------------------------------------------------------------------------- /lib/components/Interactive/converters/MouseConverter/stop.js: -------------------------------------------------------------------------------- 1 | module.exports = (container, handlers) => { 2 | // Stop mouse conversion 3 | // 4 | // Parameters 5 | // container 6 | // HTMLElement 7 | // 8 | const h = handlers 9 | const c = container 10 | 11 | c.removeEventListener('mousedown', h.onmousedown) 12 | c.removeEventListener('mousemove', h.onmousemove) 13 | c.removeEventListener('mouseup', h.onmouseup) 14 | c.removeEventListener('mouseleave', h.onmouseleave) 15 | 16 | c.removeEventListener('ratstart', h.onratstart) 17 | c.removeEventListener('ratmove', h.onratmove) 18 | c.removeEventListener('ratend', h.onratend) 19 | } 20 | -------------------------------------------------------------------------------- /lib/components/Interactive/focus.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Interactive:focus() 3 | // 4 | // Subclasses should implement this method. 5 | // 6 | throw new Error('Interactive subclass should implement ' + 7 | 'component.focus method.') 8 | } 9 | -------------------------------------------------------------------------------- /lib/components/Interactive/getCapturer.js: -------------------------------------------------------------------------------- 1 | module.exports = function (capturerName) { 2 | // @Interactive:getCapturer(capturerName) 3 | // 4 | // Get specific capturer if the component has one. Null if does not. 5 | // 6 | // Parameters: 7 | // capturerName 8 | // a string, for example 'wheel' 9 | // 10 | // Return 11 | // a Capturer, or null if not available. 12 | // 13 | const capturer = this.capturers[capturerName] 14 | 15 | if (capturer) { 16 | return capturer 17 | } 18 | 19 | return null 20 | } 21 | -------------------------------------------------------------------------------- /lib/components/Interactive/getInteraction.js: -------------------------------------------------------------------------------- 1 | module.exports = function (name) { 2 | // @Interactive:getInteraction(name) 3 | // 4 | // Get an interaction. Might be null. 5 | // 6 | // Return 7 | // an Interaction or null if no such interaction is registered. 8 | // 9 | return this.interactions[name] 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/Interactive/hasCapturer.js: -------------------------------------------------------------------------------- 1 | module.exports = function (capturerName) { 2 | // @Interactive:hasCapturer(capturerName) 3 | // 4 | // Test if the component has an active capturer. 5 | // 6 | // Parameters: 7 | // capturerName 8 | // a string, for example 'wheel' 9 | // 10 | // Return 11 | // this, for chaining 12 | // 13 | const capturer = this.capturers[capturerName] 14 | 15 | if (capturer) { 16 | return true 17 | } 18 | 19 | return false 20 | } 21 | -------------------------------------------------------------------------------- /lib/components/Interactive/hasInteraction.js: -------------------------------------------------------------------------------- 1 | module.exports = function (name) { 2 | // @Interactive:hasInteraction(name) 3 | // 4 | // Test if the component has an active interaction. 5 | // 6 | // Example: 7 | // ``` 8 | // > item.hasInteraction('tap') 9 | // false 10 | // ``` 11 | // 12 | // Return 13 | // a boolean 14 | // 15 | return name in this.interactions 16 | } 17 | -------------------------------------------------------------------------------- /lib/components/Interactive/removeAllInteractions.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Interactive:removeAllInteractions() 3 | // 4 | // Unregister all interactions. 5 | // 6 | // return 7 | // this, for chaining 8 | // 9 | const names = Object.keys(this.interactions) 10 | 11 | for (let i = 0; i < names.length; i += 1) { 12 | this.removeInteraction(names[i]) 13 | } 14 | 15 | return this 16 | } 17 | -------------------------------------------------------------------------------- /lib/components/Interactive/removeInteraction.js: -------------------------------------------------------------------------------- 1 | module.exports = function (name) { 2 | // @Interactive:removeInteraction(name) 3 | // 4 | // Unregister an interaction. If interaction does not exist, 5 | // does nothing. 6 | // 7 | // Parameters: 8 | // name 9 | // a string, name of the interaction to remove. 10 | // 11 | // return 12 | // this, for chaining 13 | // 14 | const intera = this.interactions[name] 15 | 16 | if (intera) { 17 | intera.unbind() 18 | delete this.interactions[name] 19 | } 20 | 21 | return this 22 | } 23 | -------------------------------------------------------------------------------- /lib/components/Interactive/stopCapturer.js: -------------------------------------------------------------------------------- 1 | module.exports = function (capturerName) { 2 | // @Interactive:stopCapturer(capturerName) 3 | // 4 | // Remove element listeners and the capturer. 5 | // 6 | // Parameters: 7 | // capturerName 8 | // a string, for example 'wheel' 9 | // 10 | // Return 11 | // this, for chaining 12 | // 13 | const capturer = this.capturers[capturerName] 14 | 15 | if (capturer) { 16 | capturer.unbind() 17 | delete this.capturers[capturerName] 18 | } 19 | 20 | return this 21 | } 22 | -------------------------------------------------------------------------------- /lib/components/Item/create.js: -------------------------------------------------------------------------------- 1 | module.exports = (Item) => { 2 | return (content) => { 3 | // @tapspace.createItem(content) 4 | // 5 | // Make an affine item from HTML content. Wraps the content inside a div. 6 | // 7 | // Parameters: 8 | // content 9 | // an HTMLElement or HTML string. The given element(s) will be 10 | // ..wrapped in a div. 11 | // 12 | // Return 13 | // a Item 14 | // 15 | 16 | // Create a new wrapper element. 17 | const element = document.createElement('div') 18 | 19 | const item = new Item(element) 20 | 21 | // Insert the content 22 | item.html(content) 23 | 24 | return item 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/components/Item/disable.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Item:disable() 3 | // 4 | // Makes the item non-operational, a kind of anti-interaction. 5 | // In addition to removing all ongoing interactions, it prevents 6 | // the default actions of its parents and ancestors. 7 | // 8 | // Return 9 | // this, for chaining 10 | // 11 | 12 | this.removeAllInteractions() 13 | 14 | this.capturer('gesture', { 15 | preventDefault: true 16 | }) 17 | 18 | // TODO prevent default in other capturers too. 19 | 20 | return this 21 | } 22 | -------------------------------------------------------------------------------- /lib/components/Item/holdable.js: -------------------------------------------------------------------------------- 1 | const Hold = require('../../interaction/Hold') 2 | 3 | module.exports = function (options) { 4 | // @Item:holdable(options) 5 | // 6 | // Make the item holdable and emit hold events. See interaction.Hold. 7 | // 8 | // Return 9 | // this, for chaining 10 | // 11 | 12 | // False to unbind 13 | if (options === false) { 14 | this.removeInteraction('hold') 15 | return this 16 | } 17 | 18 | // Begin hold interaction. 19 | const hold = new Hold(this, this, options) 20 | this.addInteraction('hold', hold) 21 | 22 | return this 23 | } 24 | -------------------------------------------------------------------------------- /lib/components/Item/resizable.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taataa/tapspace/d65bd79a24b724c43735b107eba4bdc3f41d6df8/lib/components/Item/resizable.js -------------------------------------------------------------------------------- /lib/components/Node/create.js: -------------------------------------------------------------------------------- 1 | module.exports = (Node) => { 2 | return (radius, color) => { 3 | // @tapspace.createNode(radius) 4 | // 5 | // Make a circle-shaped element. 6 | // 7 | // Parameters: 8 | // radius 9 | // a number 10 | // color 11 | // optional CSS color string. 12 | // Omit if you want to control the color via CSS classes. 13 | // 14 | // Return 15 | // a Node 16 | // 17 | return new Node(radius, color) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/components/Node/getBoundingCircle.js: -------------------------------------------------------------------------------- 1 | const Circle = require('../../geometry/Circle') 2 | 3 | module.exports = function () { 4 | // @Node:getBoundingCircle() 5 | // 6 | // Get the bounding circle of the node. 7 | // Takes into account the circle shape of the node. 8 | // 9 | // Return: 10 | // a Circle 11 | // 12 | const r = this.size.w / 2 13 | 14 | const circle = { x: r, y: r, z: 0, r } 15 | 16 | return new Circle(this, circle) 17 | } 18 | -------------------------------------------------------------------------------- /lib/components/Node/getDiameter.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../../geometry/Distance') 2 | 3 | module.exports = function () { 4 | // @Node:getDiameter() 5 | // 6 | // Get the circle diameter. 7 | // 8 | // Returns: 9 | // a Distance 10 | // 11 | return new Distance(this, this.size.w) 12 | } 13 | -------------------------------------------------------------------------------- /lib/components/Node/getRadius.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../../geometry/Distance') 2 | 3 | module.exports = function () { 4 | // @Node:getRadius() 5 | // 6 | // Get the circle radius. 7 | // 8 | // Returns: 9 | // a Distance 10 | // 11 | const r = this.size.w / 2 12 | 13 | return new Distance(this, r) 14 | } 15 | -------------------------------------------------------------------------------- /lib/components/Space/create.js: -------------------------------------------------------------------------------- 1 | module.exports = (Space) => { 2 | return function () { 3 | // @tapspace.createSpace() 4 | // @Space.create 5 | // 6 | // Create a container for items and other spaces. 7 | // 8 | // Return 9 | // a Space 10 | // 11 | return new Space() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/components/Transformer/dom/applyTransformOrigin.js: -------------------------------------------------------------------------------- 1 | module.exports = function (el, origin) { 2 | // Update transform-origin css property. 3 | // 4 | // Parameters: 5 | // el 6 | // an HTMLElement 7 | // origin 8 | // a point {x, y} 9 | // 10 | el.style.transformOrigin = origin.x + 'px ' + origin.y + 'px' 11 | } 12 | -------------------------------------------------------------------------------- /lib/components/Transformer/getPosition.js: -------------------------------------------------------------------------------- 1 | const point3 = require('affineplane').point3 2 | const Point = require('../../geometry/Point') 3 | 4 | module.exports = function () { 5 | // @Transformer:getPosition() 6 | // 7 | // Get the position of the transformer anchor, represented on the parent. 8 | // The main difference between getPosition and atAnchor is that getPosition 9 | // will return null if the plane has no parent, where atAnchor will return 10 | // the local anchor point regardless. 11 | // 12 | // Return 13 | // a Point or null if no parent. 14 | // 15 | 16 | const par = this.getParent() 17 | 18 | if (par) { 19 | const pos = point3.transitFrom(this.anchor, this.tran) 20 | return new Point(par, pos) 21 | } 22 | 23 | return null 24 | } 25 | -------------------------------------------------------------------------------- /lib/components/Transformer/getVectorTo.js: -------------------------------------------------------------------------------- 1 | module.exports = function (target) { 2 | // @Transformer:getVectorTo(target) 3 | // 4 | // Get vector from this component's anchor to the target. 5 | // If target does not have an anchor, we assume (0,0) for the anchor. 6 | // 7 | // Parameters 8 | // target 9 | // a Component, Basis, or Point 10 | // 11 | // Return 12 | // a Vector, represented on this plane. 13 | // 14 | let point 15 | if (target.isPoint) { 16 | point = target 17 | } else if (target.atAnchor) { 18 | point = target.atAnchor() 19 | } else if (target.at) { 20 | point = target.at(0, 0) 21 | } else { 22 | throw new Error('Invalid target argument: ' + target) 23 | } 24 | 25 | return this.atAnchor().getVectorTo(point) 26 | } 27 | -------------------------------------------------------------------------------- /lib/components/Transformer/matchPoint.js: -------------------------------------------------------------------------------- 1 | module.exports = function (source, target) { 2 | // @Transformer:matchPoint(source, target) 3 | // 4 | // Moves the element so that source point position on the element 5 | // matches the target position. 6 | // 7 | // Parameters: 8 | // source 9 | // a Point 10 | // target 11 | // a Point 12 | // 13 | // Return 14 | // this, for chaining 15 | // 16 | const v = source.getVectorTo(target) 17 | this.translateBy(v) 18 | 19 | return this 20 | } 21 | -------------------------------------------------------------------------------- /lib/components/Transformer/rotateByDegrees.js: -------------------------------------------------------------------------------- 1 | const factor = Math.PI / 180 2 | 3 | module.exports = function (degrees, pivot) { 4 | // @Transformer:rotateByDegrees(degrees[, pivot]) 5 | // 6 | // Rotate the element by degrees around z axis of an optional pivot point. 7 | // 8 | // Parameters: 9 | // degrees 10 | // a number, delta angle to rotate. 11 | // pivot 12 | // optional Point. Rotation is performed around this point. 13 | // .. Defaults to the transformer anchor. 14 | // 15 | // Return 16 | // this, for chaining 17 | // 18 | const rads = degrees * factor 19 | return this.rotateBy(rads, pivot) 20 | } 21 | -------------------------------------------------------------------------------- /lib/components/Transformer/snapGrid.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grid, anchor) { 2 | /// @Transformer:snapGrid(grid, anchor) 3 | // 4 | // Snap the transformer anchor to the closest matching grid position. 5 | // 6 | // Parameters: 7 | // grid 8 | // a Grid 9 | // anchor 10 | // optional Point on this plane. 11 | // 12 | throw new Error('not implemented') 13 | } 14 | -------------------------------------------------------------------------------- /lib/components/Viewport/addControl.js: -------------------------------------------------------------------------------- 1 | module.exports = function (control, position) { 2 | // @Viewport:addControl(control, position) 3 | // 4 | // Add new control to the viewport. 5 | // Controls do not move with the space. 6 | // 7 | // Parameters 8 | // control 9 | // a ViewportControl 10 | // position 11 | // optional {x,y} on the viewport or a Point. 12 | // 13 | // Return 14 | // this, for chaining 15 | // 16 | 17 | this.controls.addChild(control, position) 18 | 19 | // Register the buttons to control this viewport. 20 | if (control.bind) { 21 | control.bind(this) 22 | } 23 | 24 | return this 25 | } 26 | -------------------------------------------------------------------------------- /lib/components/Viewport/atNorm.js: -------------------------------------------------------------------------------- 1 | const Point = require('../../geometry/Point') 2 | 3 | module.exports = function (rx, ry, rz) { 4 | // @Viewport:atNorm(rx, ry[, rz]) 5 | // 6 | // Get a Point by relative coordinates. 7 | // 8 | // Parameters: 9 | // rx 10 | // number. 0 at left edge, 1 at right edge. 11 | // ry 12 | // number. 0 at top edge, 1 at bottom edge. 13 | // rz 14 | // optional number. 0 at projection image plane, 1 at height depth. 15 | // 16 | // Return 17 | // a Point on the element 18 | // 19 | 20 | const w = this.element.offsetWidth 21 | const h = this.element.offsetHeight 22 | 23 | const x = rx * w 24 | const y = ry * h 25 | 26 | let z = 0 27 | if (rz && typeof rz === 'number') { 28 | z = rz * h 29 | } 30 | 31 | return new Point(this, { x, y, z }) 32 | } 33 | -------------------------------------------------------------------------------- /lib/components/Viewport/getAspectRatio.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Viewport:getAspectRatio() 3 | // 4 | // Get width to height ratio of the viewport. 5 | // 6 | // Return 7 | // a number 8 | // 9 | const size = this.getSize().size 10 | return size.w / size.h 11 | } 12 | -------------------------------------------------------------------------------- /lib/components/Viewport/getControls.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Viewport:getControls() 3 | // 4 | // Return all control components of the viewport. 5 | // 6 | // Return 7 | // array of ViewportControl 8 | // 9 | return this.controls.getChildren() 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/Viewport/getHeight.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../../geometry/Distance') 2 | 3 | module.exports = function () { 4 | // @Viewport:getHeight() 5 | // 6 | // Get viewport height as a Distance. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | return new Distance(this, this.element.offsetHeight) 12 | } 13 | -------------------------------------------------------------------------------- /lib/components/Viewport/getHyperspace.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Viewport:getHyperspace() 3 | // 4 | // Get the hyperspace controlled by this viewport. 5 | // 6 | // Return 7 | // a Hyperspace 8 | // 9 | return this.hyperspace 10 | } 11 | -------------------------------------------------------------------------------- /lib/components/Viewport/getNavigationBasis.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Viewport:getNavigationBasis() 3 | // 4 | // The navigation basis determines the default pinch and zoom depth 5 | // when the user interacts with the viewport background. 6 | // 7 | // Return 8 | // a Component, the current navigation basis. 9 | // 10 | return this.navigationBasis 11 | } 12 | -------------------------------------------------------------------------------- /lib/components/Viewport/getSize.js: -------------------------------------------------------------------------------- 1 | const Size = require('../../geometry/Size') 2 | 3 | module.exports = function () { 4 | // @Viewport:getSize() 5 | // 6 | // Get viewport size. The size is read from the viewport 7 | // element.offsetWidth and element.offsetHeight. 8 | // 9 | // Return 10 | // a Size 11 | // 12 | 13 | // clientWidth and clientHeight include margin but not border. 14 | // offsetWidth and offsetHeight include margin and border. 15 | // Reference: 16 | // https://www.javascripttutorial.net/javascript-dom/javascript-width-height/ 17 | const size = { 18 | w: this.element.offsetWidth, 19 | h: this.element.offsetHeight, 20 | d: 0 21 | } 22 | 23 | return new Size(this, size) 24 | } 25 | -------------------------------------------------------------------------------- /lib/components/Viewport/getSpaces.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Viewport:getSpaces() 3 | // 4 | // Get spaces attached to the viewport. 5 | // In other words, get all children of the viewport hyperspace. 6 | // 7 | // Return 8 | // array of Component 9 | // 10 | return this.hyperspace.getChildren() 11 | } 12 | -------------------------------------------------------------------------------- /lib/components/Viewport/getWidth.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../../geometry/Distance') 2 | 3 | module.exports = function () { 4 | // @Viewport:getWidth() 5 | // 6 | // Get viewport width as a Distance. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | return new Distance(this, this.element.offsetWidth) 12 | } 13 | -------------------------------------------------------------------------------- /lib/components/Viewport/measureDilation.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const plane3 = fine.plane3 3 | 4 | module.exports = function (component) { 5 | // @Viewport:measureDilation(component) 6 | // 7 | // Measure dilation of the given component with respect to the viewport. 8 | // In other words, get component's scale relative to the viewport. 9 | // 10 | // Parameters: 11 | // component 12 | // a Component 13 | // 14 | // Returns: 15 | // a number, the dilation. 16 | // 17 | 18 | const basis = component.getTransitionTo(this) 19 | return plane3.getScale(basis) 20 | } 21 | -------------------------------------------------------------------------------- /lib/components/Viewport/prependChild.js: -------------------------------------------------------------------------------- 1 | module.exports = function (space, position) { 2 | // @Viewport:prependChild(space, position) 3 | // 4 | // Add a Space as the first child of the Viewport Hyperspace. 5 | // See also Viewport:addChild 6 | // 7 | // Parameters: 8 | // space 9 | // a Space. 10 | // position 11 | // optional Point. The position for the space origin. 12 | // 13 | // Returns: 14 | // this, for chaining 15 | // 16 | 17 | this.hyperspace.prependChild(space, position) 18 | 19 | return this 20 | } 21 | -------------------------------------------------------------------------------- /lib/components/Viewport/removeChild.js: -------------------------------------------------------------------------------- 1 | module.exports = function (space) { 2 | // @Viewport:removeChild(space) 3 | // 4 | // Remove a Component from the Viewport Hyperspace. 5 | // 6 | // Parameters: 7 | // space 8 | // a Component 9 | // 10 | // Returns: 11 | // this, for chaining 12 | // 13 | 14 | this.hyperspace.removeChild(space) 15 | 16 | return this 17 | } 18 | -------------------------------------------------------------------------------- /lib/components/Viewport/removeControl.js: -------------------------------------------------------------------------------- 1 | module.exports = function (control) { 2 | // @Viewport:removeControl(control) 3 | // 4 | // Remove control from the viewport. 5 | // 6 | // Parameters 7 | // control 8 | // a ViewportControl 9 | // 10 | // Return 11 | // this, for chaining 12 | // 13 | 14 | // Unregister all listeners. 15 | if (control.unbind) { 16 | control.unbind() 17 | } 18 | 19 | // Detach from document 20 | const controlElem = control.element 21 | const containerElem = this.controls.element 22 | containerElem.removeChild(controlElem) 23 | 24 | return this 25 | } 26 | -------------------------------------------------------------------------------- /lib/components/Viewport/renderTransform.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Viewport:renderTransform() 3 | // 4 | // You should never call this method for Viewport. 5 | // This method overrides Transformer:renderTransform and 6 | // its sole purpose is to throw an error if accidentally called. 7 | // 8 | // See also Hyperspace:renderTransform 9 | // 10 | throw new Error('Unexpected Viewport:renderTransform call.') 11 | } 12 | -------------------------------------------------------------------------------- /lib/components/Viewport/requestIdle.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Viewport:requestIdle() 3 | // 4 | // Targeted for internal use. 5 | // 6 | // Ask viewport to emit an idle event. 7 | // The idle events are used for computationally heavy tasks. 8 | // Therefore this method attempts to limit the frequency of idle events 9 | // by throttling and respecting ongoing animations. 10 | // 11 | // Return 12 | // this, for chaining 13 | // 14 | 15 | if (this.idleTimeout) { 16 | clearTimeout(this.idleTimeout) 17 | } 18 | 19 | this.idleTimeout = setTimeout(() => { 20 | if (!this.ontransitionend && !this.hyperspace.ontransitionend) { 21 | this.emit('idle') 22 | } 23 | // else idle will be requested later in ontransitionend 24 | }, 150) 25 | 26 | return this 27 | } 28 | -------------------------------------------------------------------------------- /lib/components/Viewport/setOrientation.js: -------------------------------------------------------------------------------- 1 | module.exports = function (orient, pivot) { 2 | // @Viewport:setOrientation(orient[, pivot]) 3 | // 4 | // Rotate the viewport so that its orientation matches the given orientation. 5 | // The rotation is performed around a pivot point. 6 | // 7 | // Parameters: 8 | // orient 9 | // an Orientation 10 | // pivot 11 | // optional Point, the transform origin for the rotation. 12 | // 13 | // Return 14 | // this, for chaining 15 | // 16 | 17 | // Normalize 18 | if (orient.transitRaw) { 19 | orient = orient.transitRaw(this) 20 | } 21 | 22 | // Get orientation offset 23 | const targetRot = Math.atan2(orient.b, orient.a) 24 | 25 | // Correct orientation 26 | this.rotateBy(targetRot, pivot) 27 | 28 | return this 29 | } 30 | -------------------------------------------------------------------------------- /lib/components/ViewportControl/index.js: -------------------------------------------------------------------------------- 1 | const FrameComponent = require('../FrameComponent') 2 | 3 | const ViewportControl = function (element) { 4 | // @ViewportControl(element) 5 | // 6 | // Inherits FrameComponent 7 | // 8 | // Base class for viewport control areas. 9 | // 10 | // Parameters: 11 | // element 12 | // an HTMLElement. The element does not need to be in DOM. 13 | // 14 | 15 | // Inherit 16 | FrameComponent.call(this, element) 17 | 18 | // Set class name 19 | element.classList.add('affine-control') 20 | } 21 | 22 | module.exports = ViewportControl 23 | const proto = ViewportControl.prototype 24 | proto.isViewportControl = true 25 | 26 | // Inherit 27 | Object.assign(proto, FrameComponent.prototype) 28 | -------------------------------------------------------------------------------- /lib/components/ViewportControls/index.js: -------------------------------------------------------------------------------- 1 | const Component = require('../Component') 2 | 3 | const ViewportControls = function () { 4 | // @ViewportControls() 5 | // 6 | // Inherits Component 7 | // 8 | // A container for viewport controls. 9 | // Designed to be used internally by Viewport. 10 | // Cannot be moved. 11 | // 12 | const elem = document.createElement('div') 13 | elem.className = 'affine-controls' 14 | 15 | // Use default anchor 0,0 16 | Component.call(this, elem) 17 | } 18 | 19 | const proto = ViewportControls.prototype 20 | module.exports = ViewportControls 21 | proto.isViewportControls = true 22 | 23 | // Inherit 24 | Object.assign(proto, Component.prototype) 25 | 26 | // Methods 27 | proto.adapt = require('./adapt') 28 | // TODO proto.getControls 29 | -------------------------------------------------------------------------------- /lib/effects/index.js: -------------------------------------------------------------------------------- 1 | // @tapspace.effects 2 | // 3 | // Effects are animations that can be triggered 4 | // programmatically for example as part of interaction. 5 | // 6 | 7 | exports.press = require('./press') 8 | -------------------------------------------------------------------------------- /lib/geometry/Area/changeBasis.js: -------------------------------------------------------------------------------- 1 | const scalar2 = require('affineplane').scalar2 2 | 3 | module.exports = function (newBasis) { 4 | // @Area:changeBasis(newBasis) 5 | // 6 | // The basis change of area changes the basis and represents the same area 7 | // in the target basis. Only change in scale affects the representation. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // an Area 15 | // 16 | const pr = this.basis.getTransitionTo(newBasis) 17 | const pa = scalar2.transitFrom(this.area, pr) 18 | const Area = this.constructor 19 | return new Area(newBasis, pa) 20 | } 21 | -------------------------------------------------------------------------------- /lib/geometry/Area/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Area:getRaw() 3 | // 4 | // Return plain area as a number without basis data. 5 | // 6 | return this.area 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Area/transitRaw.js: -------------------------------------------------------------------------------- 1 | const scalar2 = require('affineplane').scalar2 2 | 3 | module.exports = function (newBasis) { 4 | // @Area:transitRaw(newBasis) 5 | // 6 | // Represent the area in another basis. 7 | // Unlike changeBasis, returns a plain number without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a scalar2, a number. The area in the new basis. 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return scalar2.transitFrom(this.area, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Basis/almostEqual.js: -------------------------------------------------------------------------------- 1 | const plane3 = require('affineplane').plane3 2 | const almostEqual = plane3.almostEqual 3 | 4 | module.exports = function (b, tolerance) { 5 | // @Basis:almostEqual(b[, tolerance]) 6 | // 7 | // Test if this is equal to the given basis b within tolerance. 8 | // Almost equality requires identical basis' basis 9 | // and that coordinates are equal within tolerance. 10 | // 11 | // Parameters: 12 | // b 13 | // a Basis 14 | // tolerance 15 | // optional number. Maximum tolerated distance between coordinates. 16 | // 17 | // Return 18 | // a boolean 19 | // 20 | if (!b || !b.basis) { 21 | return false 22 | } 23 | 24 | return this.basis === b.basis && almostEqual(this.tran, b.tran, tolerance) 25 | } 26 | -------------------------------------------------------------------------------- /lib/geometry/Basis/at.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const plane3 = fine.plane3 3 | const Point = require('../Point') 4 | 5 | module.exports = function (x, y, z) { 6 | // @Basis:at(x, y, z) 7 | // @Basis:getPoint 8 | // 9 | // Get a point using the internal coordinates of the basis. 10 | // 11 | // Parameters: 12 | // x 13 | // a number 14 | // y 15 | // a number 16 | // z 17 | // optional number, zero by default. 18 | // 19 | // Return: 20 | // a Point 21 | // 22 | 23 | // Allow undefined z 24 | if (!z) { z = 0 } 25 | 26 | const p = plane3.at(this.tran, x, y, z) 27 | return new Point(this.basis, p) 28 | } 29 | -------------------------------------------------------------------------------- /lib/geometry/Basis/changeBasis.js: -------------------------------------------------------------------------------- 1 | const plane3 = require('affineplane').plane3 2 | 3 | module.exports = function (newBasis) { 4 | // @Basis:changeBasis(newBasis) 5 | // 6 | // Transit the virtual basis to another element basis. 7 | // In other words, represent the same basis 8 | // in the coordinate system of the given element basis. 9 | // 10 | // Parameters: 11 | // newBasis 12 | // a Component 13 | // 14 | // Return 15 | // a Basis 16 | // 17 | const pr = this.basis.getTransitionTo(newBasis) 18 | const pb = plane3.transitFrom(this.tran, pr) 19 | const Basis = this.constructor 20 | return new Basis(newBasis, pb) 21 | } 22 | -------------------------------------------------------------------------------- /lib/geometry/Basis/createDistance.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const plane3 = fine.plane3 3 | const Distance = require('../../geometry/Distance') 4 | 5 | module.exports = function (d) { 6 | // @Basis:createDistance(d) 7 | // 8 | // Get a distance on the virtual basis. 9 | // 10 | // Parameters: 11 | // d 12 | // a number, the distance represented in the virtual basis. 13 | // 14 | // Return 15 | // a Distance 16 | // 17 | 18 | const m = plane3.getScale(this.tran) 19 | return new Distance(this.basis, m * d) 20 | } 21 | -------------------------------------------------------------------------------- /lib/geometry/Basis/createVector.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const vec3 = fine.vec3 3 | const Vector = require('../../geometry/Vector') 4 | 5 | module.exports = function (x, y, z) { 6 | // @Basis:createVector(x, y[, z]) 7 | // 8 | // Create a vector using coordinate system of this basis. 9 | // 10 | // Parameters: 11 | // x 12 | // a number 13 | // y 14 | // a number 15 | // z 16 | // optional number, default is 0. 17 | // 18 | // Return 19 | // a Vector 20 | // 21 | 22 | // Build raw 23 | const v = { x, y, z: z || 0 } 24 | // We need to represent the vector on the element basis. 25 | const vb = vec3.transitFrom(v, this.tran) 26 | // Build tensor 27 | return new Vector(this.basis, vb) 28 | } 29 | -------------------------------------------------------------------------------- /lib/geometry/Basis/equal.js: -------------------------------------------------------------------------------- 1 | const plane3 = require('affineplane').plane3 2 | const equal = plane3.equal 3 | 4 | module.exports = function (b) { 5 | // @Basis:equal(b) 6 | // 7 | // Test if the value b is a Basis and strictly equal. 8 | // Strict equality requires the same basis' basis 9 | // and identical coordinates. 10 | // 11 | // Parameters: 12 | // b 13 | // a Basis 14 | // 15 | // Return 16 | // a boolean 17 | // 18 | if (!b || !b.basis) { 19 | return false 20 | } 21 | 22 | return this.basis === b.basis && equal(this.tran, b.tran) 23 | } 24 | -------------------------------------------------------------------------------- /lib/geometry/Basis/getOrientation.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const plane3 = fine.plane3 3 | const Orientation = require('../../geometry/Orientation') 4 | 5 | module.exports = function () { 6 | // @Basis:getOrientation() 7 | // 8 | // Get the orientation of this virtual basis. 9 | // Provides a way to match the orientation between components and geometry. 10 | // 11 | // Example: 12 | // ``` 13 | // > const orient = item.getBasis().rotateByDegrees(45).getOrientation() 14 | // > anotherItem.setOrientation(orient) 15 | // ``` 16 | // 17 | // Return 18 | // an Orientation 19 | // 20 | const orient = plane3.orientation(this.tran) 21 | return new Orientation(this.basis, orient) 22 | } 23 | -------------------------------------------------------------------------------- /lib/geometry/Basis/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Basis:getRaw() 3 | // 4 | // Return plain plane3 object `{ a, b, x, y, z }` without element basis. 5 | // 6 | return this.tran 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Basis/getScale.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const plane3 = fine.plane3 3 | const Scale = require('../../geometry/Scale') 4 | 5 | module.exports = function () { 6 | // @Basis:getScale() 7 | // 8 | // Get the scale of this virtual basis. 9 | // Provides a way to measure and match 10 | // the scale between components and geometry. 11 | // 12 | // Return 13 | // a Scale 14 | // 15 | const scale = plane3.getScale(this.tran) 16 | return new Scale(this.basis, scale) 17 | } 18 | -------------------------------------------------------------------------------- /lib/geometry/Basis/getTransformTo.js: -------------------------------------------------------------------------------- 1 | const plane3 = require('affineplane').plane3 2 | 3 | module.exports = (Transform) => { 4 | // Use factory pattern to control circular dependency. 5 | return function (b) { 6 | // @Basis:getTransformTo(b) 7 | // 8 | // Get a transform from this to the basis b. 9 | // The resulting transformation would transform this basis 10 | // to the given basis when applied to the origin. 11 | // 12 | // Parameters: 13 | // b 14 | // a Basis 15 | // 16 | // Return: 17 | // a Transform on this basis. 18 | // 19 | 20 | // Normalize to plane3 21 | if (b.transitRaw) { 22 | b = b.transitRaw(this.basis) 23 | } 24 | 25 | const bOnThis = plane3.transitTo(b, this.tran) 26 | return new Transform(this.basis, bOnThis) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/geometry/Basis/rotateByDegrees.js: -------------------------------------------------------------------------------- 1 | const factor = Math.PI / 180 2 | 3 | module.exports = function (degrees, origin) { 4 | // @Basis:rotateByDegrees(degrees[, origin]) 5 | // 6 | // Rotate the basis around the z-axis that intersects the origin point. 7 | // 8 | // Parameters: 9 | // degrees 10 | // a number, the amount to rotate in degrees. 11 | // origin 12 | // optional Point, the transform origin. Defaults to the basis origin. 13 | // 14 | // Return 15 | // a Basis, the rotated basis 16 | // 17 | if (typeof degrees !== 'number') { 18 | throw new Error('Invalid rotation angle: ' + degrees) 19 | } 20 | 21 | const rads = degrees * factor 22 | return this.rotateBy(rads, origin) 23 | } 24 | -------------------------------------------------------------------------------- /lib/geometry/Basis/transitRaw.js: -------------------------------------------------------------------------------- 1 | const plane3 = require('affineplane').plane3 2 | 3 | module.exports = function (newBasis) { 4 | // @Basis:transitRaw(newBasis) 5 | // 6 | // Represent the virtual basis on another element basis. 7 | // Unlike changeBasis, returns a plain object without element basis. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a plane3, an object. 15 | // 16 | const tr = this.basis.getTransitionTo(newBasis) 17 | return plane3.transitFrom(this.tran, tr) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Basis/transitRawOuter.js: -------------------------------------------------------------------------------- 1 | const plane3 = require('affineplane').plane3 2 | 3 | module.exports = function (basis) { 4 | // @Basis:transitRawOuter(basis) 5 | // 6 | // Represent the virtual basis on the parent of the element basis. 7 | // Unlike changeBasis, returns a plain object without the element basis. 8 | // This corresponds to `Basis:changeBasis(basis.getParent()).getRaw()` 9 | // but is more efficient and does not require the parent to exist. 10 | // 11 | // Parameters: 12 | // basis 13 | // a Component 14 | // 15 | // Return 16 | // a plane3, an object. 17 | // 18 | const tr = this.basis.getTransitionToParentOf(basis) 19 | return plane3.transitFrom(this.tran, tr) 20 | } 21 | -------------------------------------------------------------------------------- /lib/geometry/Basis/translateBy.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const plane3 = fine.plane3 3 | 4 | module.exports = function (vec) { 5 | // @Basis:translateBy(vec) 6 | // 7 | // Translate the virtual basis by a vector. 8 | // 9 | // Parameters: 10 | // vec 11 | // a Vector, the translation 12 | // 13 | // Return 14 | // a Basis, the translated virtual basis 15 | // 16 | 17 | // Normalize 18 | if (vec.transitRaw) { 19 | vec = vec.transitRaw(this.basis) 20 | } 21 | 22 | const ttran = plane3.translateBy(this.tran, vec) 23 | 24 | const Basis = this.constructor 25 | return new Basis(this.basis, ttran) 26 | } 27 | -------------------------------------------------------------------------------- /lib/geometry/Box/almostEqual.js: -------------------------------------------------------------------------------- 1 | const box3 = require('affineplane').box3 2 | const almostEqual = box3.almostEqual 3 | 4 | module.exports = function (b, tolerance) { 5 | // @Box:almostEqual(b[, tolerance]) 6 | // 7 | // Test if this box is equal to the given box b within tolerance. 8 | // Almost equality requires identical basis 9 | // and that box size, position, and orientation are equal within tolerance. 10 | // 11 | // Parameters: 12 | // b 13 | // a Box 14 | // tolerance 15 | // optional number. Maximum tolerated distance between values. 16 | // 17 | // Return 18 | // a boolean 19 | // 20 | if (!b || !b.basis) { 21 | return false 22 | } 23 | 24 | return this.basis === b.basis && almostEqual(this.box, b.box, tolerance) 25 | } 26 | -------------------------------------------------------------------------------- /lib/geometry/Box/at.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const box3 = fine.box3 3 | const Point = require('../Point') 4 | 5 | module.exports = function (x, y, z) { 6 | // @Box:at(x, y[, z]) 7 | // @Box:getPoint 8 | // 9 | // Get a point using the internal coordinates of the box. 10 | // 11 | // Parameters: 12 | // x 13 | // a number 14 | // y 15 | // a number 16 | // z 17 | // optional number, zero by default. 18 | // 19 | // Return: 20 | // a Point 21 | // 22 | 23 | // Allow undefined z 24 | if (!z) { z = 0 } 25 | 26 | const p = box3.at(this.box, x, y, z) 27 | return new Point(this.basis, p) 28 | } 29 | -------------------------------------------------------------------------------- /lib/geometry/Box/atCenter.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const box3 = fine.box3 3 | const Point = require('../Point') 4 | 5 | module.exports = function () { 6 | // @Box:atCenter() 7 | // @Box:atMid() 8 | // 9 | // Get the center point of the box. 10 | // 11 | // Return 12 | // a Point 13 | // 14 | 15 | const p = box3.atNorm(this.box, 0.5, 0.5, 0.5) 16 | return new Point(this.basis, p) 17 | } 18 | -------------------------------------------------------------------------------- /lib/geometry/Box/changeBasis.js: -------------------------------------------------------------------------------- 1 | const box3 = require('affineplane').box3 2 | 3 | module.exports = function (newBasis) { 4 | // @Box:changeBasis(newBasis) 5 | // 6 | // Transit the box to another basis. In other words, represent the same box 7 | // in the coordinate system of the given basis. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a Box 15 | // 16 | const pr = this.basis.getTransitionTo(newBasis) 17 | const pb = box3.transitFrom(this.box, pr) 18 | const Box = this.constructor 19 | return new Box(newBasis, pb) 20 | } 21 | -------------------------------------------------------------------------------- /lib/geometry/Box/equal.js: -------------------------------------------------------------------------------- 1 | const box3 = require('affineplane').box3 2 | const equal = box3.equal 3 | 4 | module.exports = function (b) { 5 | // @Box:equal(b) 6 | // 7 | // Test if this box is strictly equal with the given box b. 8 | // Strict equality requires that the boxes are on the same basis 9 | // and have strictly equal size, position, and orientation. 10 | // 11 | // Parameters: 12 | // b 13 | // a Box 14 | // 15 | // Return 16 | // a boolean 17 | // 18 | if (!b || !b.basis) { 19 | return false 20 | } 21 | 22 | return this.basis === b.basis && equal(this.box, b.box) 23 | } 24 | -------------------------------------------------------------------------------- /lib/geometry/Box/getArea.js: -------------------------------------------------------------------------------- 1 | const Area = require('../Area') 2 | 3 | module.exports = function () { 4 | // @Box:getArea() 5 | // 6 | // Get the area of the front face of the box. 7 | // 8 | // Return 9 | // an Area 10 | // 11 | 12 | const area = this.box.w * this.box.h 13 | 14 | return new Area(this.basis, area) 15 | } 16 | -------------------------------------------------------------------------------- /lib/geometry/Box/getBoundingCircle.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const box2 = fine.box2 3 | 4 | module.exports = (Circle) => { 5 | return function () { 6 | // @Box:getBoundingCircle() 7 | // 8 | // Get the circle boundary of the rectangular box front. 9 | // The circle center equals the box front center. 10 | // 11 | // Return: 12 | // a Circle 13 | // 14 | const c = box2.getCircle(this.box) 15 | c.z = this.box.z // patch 16 | return new Circle(this.basis, c) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Box/getBoundingSphere.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const box3 = fine.box3 3 | 4 | module.exports = (Sphere) => { 5 | return function () { 6 | // @Box:getBoundingSphere() 7 | // 8 | // Get the spherical boundary of the box. 9 | // The sphere center equals the box center. 10 | // 11 | // Return: 12 | // a Sphere 13 | // 14 | const sp = box3.getSphere(this.box) 15 | return new Sphere(this.basis, sp) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/geometry/Box/getDepth.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../Distance') 2 | 3 | module.exports = function () { 4 | // @Box:getDepth() 5 | // 6 | // Get box depth as a distance. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | 12 | return new Distance(this.basis, this.box.d) 13 | } 14 | -------------------------------------------------------------------------------- /lib/geometry/Box/getDiagonal.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../Distance') 2 | 3 | module.exports = function () { 4 | // @Box:getDiagonal() 5 | // 6 | // Get box diagonal as a distance. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | 12 | const w = this.box.w 13 | const h = this.box.h 14 | const diag = Math.sqrt(w * w + h * h) 15 | return new Distance(this.basis, diag) 16 | } 17 | -------------------------------------------------------------------------------- /lib/geometry/Box/getHeight.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../Distance') 2 | 3 | module.exports = function () { 4 | // @Box:getHeight() 5 | // 6 | // Get box height as a distance. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | 12 | return new Distance(this.basis, this.box.h) 13 | } 14 | -------------------------------------------------------------------------------- /lib/geometry/Box/getInnerSquare.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const box2 = fine.box2 3 | 4 | module.exports = function () { 5 | // @Box:getInnerSquare() 6 | // 7 | // Get the largest square inside the box that has the same center point. 8 | // Useful for computing areas based on the shortest box dimension. 9 | // 10 | // Return: 11 | // a Box 12 | // 13 | const Box = this.constructor 14 | const square = box2.getInnerSquare(this.box) 15 | // patch to box3 16 | square.z = 0 17 | square.d = 0 18 | return new Box(this.basis, square) 19 | } 20 | -------------------------------------------------------------------------------- /lib/geometry/Box/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Box:getRaw() 3 | // 4 | // Return plain box3 object `{ a, b, x, y, z, w, h, d }` without basis data. 5 | // 6 | return this.box 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Box/getSize.js: -------------------------------------------------------------------------------- 1 | const Size = require('../Size') 2 | 3 | module.exports = function () { 4 | // @Box:getSize() 5 | // 6 | // Get box dimensions. 7 | // 8 | // Return 9 | // a Size 10 | // 11 | 12 | // box3 is a size3 (has w,h,d properties) 13 | return new Size(this.basis, { 14 | w: this.box.w, 15 | h: this.box.h, 16 | d: this.box.d 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Box/getVolume.js: -------------------------------------------------------------------------------- 1 | const Volume = require('../Volume') 2 | 3 | module.exports = function () { 4 | // @Box:getVolume() 5 | // 6 | // Get the volume of the box. 7 | // 8 | // Return 9 | // a Volume 10 | // 11 | 12 | const vol = this.box.w * this.box.h * this.box.d 13 | 14 | return new Volume(this.basis, vol) 15 | } 16 | -------------------------------------------------------------------------------- /lib/geometry/Box/getWidth.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../Distance') 2 | 3 | module.exports = function () { 4 | // @Box:getWidth() 5 | // 6 | // Get box width as a distance. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | 12 | return new Distance(this.basis, this.box.w) 13 | } 14 | -------------------------------------------------------------------------------- /lib/geometry/Box/resizeTo.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const box3 = fine.box3 3 | 4 | module.exports = function (size, origin) { 5 | // @Box:resizeTo(size, origin) 6 | // 7 | // Resize the box to given size by keeping the origin fixed. 8 | // 9 | // Parameters: 10 | // size 11 | // a Size, the new size 12 | // origin 13 | // a Point, the fixed pivot 14 | // 15 | // Return 16 | // a Box, the translated box 17 | // 18 | 19 | // Normalize 20 | if (size.transitRaw) { 21 | size = size.transitRaw(this.basis) 22 | } 23 | if (origin.transitRaw) { 24 | origin = origin.transitRaw(this.basis) 25 | } 26 | 27 | const box = box3.resizeTo(this.box, origin, size.w, size.h, size.d) 28 | 29 | const Box = this.constructor 30 | return new Box(this.basis, box) 31 | } 32 | -------------------------------------------------------------------------------- /lib/geometry/Box/transitRaw.js: -------------------------------------------------------------------------------- 1 | const box3 = require('affineplane').box3 2 | 3 | module.exports = function (newBasis) { 4 | // @Box:transitRaw(newBasis) 5 | // 6 | // Represent the box in another basis. 7 | // Unlike changeBasis, returns a plain object without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a box3, an object. 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return box3.transitFrom(this.box, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Box/translateBy.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const box3 = fine.box3 3 | 4 | module.exports = function (vec) { 5 | // @Box:translateBy(vec) 6 | // 7 | // Translate the box by a vector. 8 | // 9 | // Parameters: 10 | // vec 11 | // a Vector, the translation 12 | // 13 | // Return 14 | // a Box, the translated box 15 | // 16 | 17 | // Normalize 18 | if (vec.transitRaw) { 19 | vec = vec.transitRaw(this.basis) 20 | } 21 | 22 | const tbox = box3.translateBy(this.box, vec.x, vec.y, vec.z) 23 | 24 | const Box = this.constructor 25 | return new Box(this.basis, tbox) 26 | } 27 | -------------------------------------------------------------------------------- /lib/geometry/Circle/almostEqual.js: -------------------------------------------------------------------------------- 1 | const circle3 = require('affineplane').circle3 2 | const almostEqual = circle3.almostEqual 3 | 4 | module.exports = function (c, tolerance) { 5 | // @Circle:almostEqual(c[, tolerance]) 6 | // 7 | // Test if this circle is equal to the given circle c within tolerance. 8 | // Almost equality requires identical basis 9 | // and that circle radius and position are equal within tolerance. 10 | // 11 | // Parameters: 12 | // c 13 | // a Circle 14 | // tolerance 15 | // optional number. Maximum tolerated distance between values. 16 | // 17 | // Return 18 | // a boolean 19 | // 20 | if (!c || !c.basis) { 21 | return false 22 | } 23 | 24 | return this.basis === c.basis && almostEqual(this.circle, c.circle, tolerance) 25 | } 26 | -------------------------------------------------------------------------------- /lib/geometry/Circle/atCenter.js: -------------------------------------------------------------------------------- 1 | const Point = require('../Point') 2 | 3 | module.exports = function () { 4 | // @Circle:atCenter() 5 | // @Circle:atMid() 6 | // 7 | // Get the center point of the circle. 8 | // 9 | // Return 10 | // a Point 11 | // 12 | 13 | return new Point(this.basis, { 14 | x: this.circle.x, 15 | y: this.circle.y, 16 | z: this.circle.z 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Circle/changeBasis.js: -------------------------------------------------------------------------------- 1 | const circle3 = require('affineplane').circle3 2 | 3 | module.exports = function (newBasis) { 4 | // @Circle:changeBasis(newBasis) 5 | // 6 | // Transit the circle to another basis. In other words, represent the same 7 | // geometry in the coordinate system of the given basis. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a Circle 15 | // 16 | const pr = this.basis.getTransitionTo(newBasis) 17 | const pb = circle3.transitFrom(this.circle, pr) 18 | const Circle = this.constructor 19 | return new Circle(newBasis, pb) 20 | } 21 | -------------------------------------------------------------------------------- /lib/geometry/Circle/equal.js: -------------------------------------------------------------------------------- 1 | const circle3 = require('affineplane').circle3 2 | const equal = circle3.equal 3 | 4 | module.exports = function (c) { 5 | // @Circle:equal(c) 6 | // 7 | // Test if this circle is strictly equal with the given circle c. 8 | // Strict equality requires that the circles are on the same basis 9 | // and have strictly equal radius and position. 10 | // 11 | // Parameters: 12 | // c 13 | // a Circle 14 | // 15 | // Return 16 | // a boolean 17 | // 18 | if (!c || !c.basis) { 19 | return false 20 | } 21 | 22 | return this.basis === c.basis && equal(this.circle, c.circle) 23 | } 24 | -------------------------------------------------------------------------------- /lib/geometry/Circle/getArea.js: -------------------------------------------------------------------------------- 1 | const circle3 = require('affineplane').circle3 2 | const Area = require('../Area') 3 | 4 | module.exports = function () { 5 | // @Circle:getArea() 6 | // 7 | // Get the area of the circle. 8 | // 9 | // Return 10 | // an Area 11 | // 12 | const ar = circle3.area(this.circle) 13 | return new Area(this.basis, ar) 14 | } 15 | -------------------------------------------------------------------------------- /lib/geometry/Circle/getDiameter.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../Distance') 2 | 3 | module.exports = function () { 4 | // @Circle:getDiameter() 5 | // @Circle:getHeight 6 | // @Circle:getWidth 7 | // 8 | // Get circle diameter as a distance. The diameter equals the circle 9 | // width, height, and depth. 10 | // 11 | // Return 12 | // a Distance 13 | // 14 | 15 | return new Distance(this.basis, this.circle.r * 2) 16 | } 17 | -------------------------------------------------------------------------------- /lib/geometry/Circle/getRadius.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../Distance') 2 | 3 | module.exports = function () { 4 | // @Circle:getRadius() 5 | // 6 | // Get radius as a distance. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | return new Distance(this.basis, this.circle.r) 12 | } 13 | -------------------------------------------------------------------------------- /lib/geometry/Circle/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Circle:getRaw() 3 | // 4 | // Return plain circle3 object `{ x, y, z, r }` without basis data. 5 | // 6 | return this.circle 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Circle/getSize.js: -------------------------------------------------------------------------------- 1 | const Size = require('../Size') 2 | 3 | module.exports = function () { 4 | // @Circle:getSize() 5 | // 6 | // Get circle dimensions. 7 | // 8 | // Return 9 | // a Size 10 | // 11 | 12 | const d = this.circle.r * 2 13 | return new Size(this.basis, { 14 | w: d, 15 | h: d, 16 | d: 0 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Circle/transitRaw.js: -------------------------------------------------------------------------------- 1 | const circle3 = require('affineplane').circle3 2 | 3 | module.exports = function (newBasis) { 4 | // @Circle:transitRaw(newBasis) 5 | // 6 | // Represent the circle in another basis. 7 | // Unlike changeBasis, returns a plain object without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a circle3, an object. 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return circle3.transitFrom(this.circle, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Circle/translateBy.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const circle3 = fine.circle3 3 | 4 | module.exports = function (vec) { 5 | // @Circle:translateBy(vec) 6 | // 7 | // Translate the circle by a vector. 8 | // 9 | // Parameters: 10 | // vec 11 | // a Vector, the translation 12 | // 13 | // Return 14 | // a Circle 15 | // 16 | 17 | // Normalize 18 | if (vec.transitRaw) { 19 | vec = vec.transitRaw(this.basis) 20 | } 21 | 22 | // Allow 2D vector 23 | vec.z = vec.z || 0 24 | 25 | const circle = circle3.translate(this.circle, vec) 26 | 27 | const Circle = this.constructor 28 | return new Circle(this.basis, circle) 29 | } 30 | -------------------------------------------------------------------------------- /lib/geometry/Direction/almostEqual.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | // TODO use dir3 3 | 4 | module.exports = function (dir, tolerance) { 5 | // @Direction:almostEqual(dir[, tolerance]) 6 | // 7 | // Test if this direction is equal to a given direction within tolerance. 8 | // Equality requires strictly equal basis and that the distance between coordinates 9 | // is within the tolerance. 10 | // 11 | // Parameters: 12 | // dir 13 | // a Direction 14 | // tolerance 15 | // optional number 16 | // 17 | // Return 18 | // a boolean 19 | // 20 | 21 | if (dir && dir.basis && this.basis === dir.basis) { 22 | return vec3.almostEqual(this.dir, dir.dir, tolerance) 23 | } 24 | 25 | return false 26 | } 27 | -------------------------------------------------------------------------------- /lib/geometry/Direction/changeBasis.js: -------------------------------------------------------------------------------- 1 | const dir3 = require('affineplane').dir3 2 | 3 | module.exports = function (newBasis) { 4 | // @Direction:changeBasis(newBasis) 5 | // 6 | // Parameters: 7 | // newBasis 8 | // a Component 9 | // 10 | // Return 11 | // a Direction 12 | // 13 | const tran = this.basis.getTransitionTo(newBasis) 14 | const tdir = dir3.transitFrom(this.dir, tran) 15 | const Direction = this.constructor 16 | return new Direction(newBasis, tdir) 17 | } 18 | -------------------------------------------------------------------------------- /lib/geometry/Direction/equal.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = function (dir) { 4 | // @Direction:equal(dir) 5 | // 6 | // Test if this direction is strictly equal to a given direction. 7 | // Strict equality requires strictly equal basis and coordinates. 8 | // 9 | // Parameters: 10 | // dir 11 | // a Direction 12 | // 13 | // Return 14 | // a boolean 15 | // 16 | 17 | if (dir && dir.basis && this.basis === dir.basis) { 18 | return vec3.equal(this.dir, dir.dir) 19 | } 20 | 21 | return false 22 | } 23 | -------------------------------------------------------------------------------- /lib/geometry/Direction/fromVector.js: -------------------------------------------------------------------------------- 1 | module.exports = (Direction) => { 2 | // Use factor pattern to avoid circular dependency. 3 | return function (basis, vec) { 4 | // @Direction:fromVector(basis, vec) 5 | // 6 | // Create a Direction from a vector. 7 | // 8 | // Parameters 9 | // basis 10 | // a Component 11 | // vec 12 | // a Vector, or {x,y,z} relative to the basis. 13 | // 14 | // Return 15 | // a Direction 16 | // 17 | return new Direction(basis, vec) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/geometry/Direction/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Direction:getRaw() 3 | // 4 | // Return plain dir3 object {x,y,z} without basis data. 5 | // 6 | return this.dir 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Direction/transitRaw.js: -------------------------------------------------------------------------------- 1 | const dir3 = require('affineplane').dir3 2 | 3 | module.exports = function (newBasis) { 4 | // @Direction:transitRaw(newBasis) 5 | // 6 | // Represent the direction in another basis. 7 | // Unlike changeBasis, returns a plain object without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a dir3 unit vector 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return dir3.transitFrom(this.dir, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Distance/changeBasis.js: -------------------------------------------------------------------------------- 1 | const dist3 = require('affineplane').dist3 2 | 3 | module.exports = function (newBasis) { 4 | // @Distance:changeBasis(newBasis) 5 | // 6 | // Parameters: 7 | // newBasis 8 | // a Component 9 | // 10 | // Return 11 | // a Distance 12 | // 13 | const tr = this.basis.getTransitionTo(newBasis) 14 | const td = dist3.transitFrom(this.dist, tr) 15 | const Distance = this.constructor 16 | return new Distance(newBasis, td) 17 | } 18 | -------------------------------------------------------------------------------- /lib/geometry/Distance/equal.js: -------------------------------------------------------------------------------- 1 | module.exports = function (dist) { 2 | // @Distance:equal(dist) 3 | // 4 | // Test if this distance is strictly equal to the given distance. 5 | // Strict equality requires strictly equal basis and numerical distance. 6 | // 7 | // Parameters: 8 | // dist 9 | // a Distance 10 | // 11 | // Return 12 | // a boolean 13 | // 14 | 15 | if (dist && dist.basis && this.basis === dist.basis) { 16 | return this.dist === dist.dist 17 | } 18 | 19 | return false 20 | } 21 | -------------------------------------------------------------------------------- /lib/geometry/Distance/getNumber.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Distance:getNumber() 3 | // 4 | // Get the distance as a number on this basis. 5 | // 6 | // Return 7 | // a number 8 | // 9 | return this.dist 10 | } 11 | -------------------------------------------------------------------------------- /lib/geometry/Distance/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Distance:getRaw() 3 | // 4 | // Return 5 | // a dist3, a number without basis data. 6 | // 7 | return this.dist 8 | } 9 | -------------------------------------------------------------------------------- /lib/geometry/Distance/getVector.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const dir3 = fine.dir3 3 | 4 | module.exports = (Vector) => { 5 | return function (dir) { 6 | // @Distance:getVector(dir) 7 | // 8 | // Parameters: 9 | // dir 10 | // a Direction 11 | // 12 | // Return 13 | // a Vector 14 | // 15 | 16 | // Normalize direction 17 | if (dir.transitRaw) { 18 | dir = dir.transitRaw(this.basis) 19 | } 20 | 21 | const vec = dir3.toVector(dir, this.dist) 22 | 23 | return new Vector(this.basis, vec) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/geometry/Distance/isGreaterThan.js: -------------------------------------------------------------------------------- 1 | module.exports = function (distance) { 2 | // @Distance:isGreaterThan(distance) 3 | // 4 | // Test if this is greater than the given distance. 5 | // 6 | // Parameters: 7 | // distance 8 | // a Distance 9 | // 10 | // Return 11 | // a boolean 12 | // 13 | if (distance.transitRaw) { 14 | distance = distance.transitRaw(this.basis) 15 | } 16 | 17 | return this.dist > distance 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Distance/isLessThan.js: -------------------------------------------------------------------------------- 1 | module.exports = function (distance) { 2 | // @Distance:isLessThan(distance) 3 | // 4 | // Test if this is less than the given distance. 5 | // 6 | // Parameters: 7 | // distance 8 | // a Distance 9 | // 10 | // Return 11 | // a boolean 12 | // 13 | if (distance.transitRaw) { 14 | distance = distance.transitRaw(this.basis) 15 | } 16 | 17 | return this.dist < distance 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Distance/max.js: -------------------------------------------------------------------------------- 1 | module.exports = function (distance) { 2 | // @Distance:max(distance) 3 | // 4 | // Return maximum of the two distances. 5 | // 6 | // Parameters: 7 | // distance 8 | // a Distance to compare. 9 | // 10 | // Returns: 11 | // a Distance 12 | // 13 | 14 | let dist = distance 15 | if (distance.transitRaw) { 16 | dist = distance.transitRaw(this.basis) 17 | } 18 | 19 | if (dist <= this.dist) { 20 | return this 21 | } 22 | // else 23 | return distance 24 | } 25 | -------------------------------------------------------------------------------- /lib/geometry/Distance/min.js: -------------------------------------------------------------------------------- 1 | module.exports = function (distance) { 2 | // @Distance:min(distance) 3 | // 4 | // Return minimum of the two distances. 5 | // 6 | // Parameters: 7 | // distance 8 | // a Distance to compare. 9 | // 10 | // Returns: 11 | // a Distance 12 | // 13 | 14 | let dist = distance 15 | if (distance.transitRaw) { 16 | dist = distance.transitRaw(this.basis) 17 | } 18 | 19 | if (dist >= this.dist) { 20 | return this 21 | } 22 | // else 23 | return distance 24 | } 25 | -------------------------------------------------------------------------------- /lib/geometry/Distance/scaleBy.js: -------------------------------------------------------------------------------- 1 | module.exports = function (multiplier) { 2 | // @Distance:scaleBy(multiplier) 3 | // @Distance:multiply 4 | // 5 | // Multiply the distance. Returns new Distance. 6 | // 7 | // Parameters 8 | // multiplier 9 | // a number 10 | // 11 | // Return 12 | // a Distance 13 | // 14 | const Distance = this.constructor 15 | return new Distance(this.basis, this.dist * multiplier) 16 | } 17 | -------------------------------------------------------------------------------- /lib/geometry/Distance/transitRaw.js: -------------------------------------------------------------------------------- 1 | const dist3 = require('affineplane').dist3 2 | 3 | module.exports = function (newBasis) { 4 | // @Distance:transitRaw(newBasis) 5 | // 6 | // Represent the distance in another basis. 7 | // Unlike changeBasis, returns a plain object without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a dist3 number 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return dist3.transitFrom(this.dist, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Grid/index.js: -------------------------------------------------------------------------------- 1 | // TODO 2 | // 3 | // The `Grid` is a tool to snap the position of 4 | // space elements to discrete xyz-lattices. 5 | // 6 | // Maybe scales and rotations too. 7 | // 8 | -------------------------------------------------------------------------------- /lib/geometry/Line/index.js: -------------------------------------------------------------------------------- 1 | const Line = function (basis, line) { 2 | // @Line(basis, line) 3 | // 4 | // A line tensor. The line extends to infinity in two directions. 5 | // 6 | // Parameters 7 | // basis 8 | // a Component 9 | // line 10 | // a line3 object `{ origin: , span: }` 11 | // 12 | this.basis = basis 13 | this.line = line 14 | } 15 | 16 | module.exports = Line 17 | const proto = Line.prototype 18 | proto.isLine = true 19 | -------------------------------------------------------------------------------- /lib/geometry/Orientation/almostEqual.js: -------------------------------------------------------------------------------- 1 | const orient2 = require('affineplane').orient2 2 | 3 | module.exports = function (orient, tolerance) { 4 | // @Orientation:almostEqual(orient[, tolerance]) 5 | // 6 | // Test if this orientation is equal to a given orientation within tolerance. 7 | // Equality requires strictly equal basis and that the difference in orientation 8 | // is within the tolerance. 9 | // 10 | // Parameters: 11 | // orient 12 | // an Orientation 13 | // tolerance 14 | // optional number 15 | // 16 | // Return 17 | // a boolean 18 | // 19 | 20 | if (orient && orient.basis && this.basis === orient.basis) { 21 | return orient2.almostEqual(this.orient, orient.orient, tolerance) 22 | } 23 | 24 | return false 25 | } 26 | -------------------------------------------------------------------------------- /lib/geometry/Orientation/changeBasis.js: -------------------------------------------------------------------------------- 1 | const orient2 = require('affineplane').orient2 2 | 3 | module.exports = function (newBasis) { 4 | // @Orientation:changeBasis(newBasis) 5 | // 6 | // Parameters: 7 | // newBasis 8 | // a Component 9 | // 10 | // Return 11 | // an Orientation 12 | // 13 | const tran = this.basis.getTransitionTo(newBasis) 14 | const orient = orient2.transitFrom(this.orient, tran) 15 | const Orientation = this.constructor 16 | return new Orientation(newBasis, orient) 17 | } 18 | -------------------------------------------------------------------------------- /lib/geometry/Orientation/equal.js: -------------------------------------------------------------------------------- 1 | const orient2 = require('affineplane').orient2 2 | 3 | module.exports = function (orient) { 4 | // @Orientation:equal(orient) 5 | // 6 | // Test if this orientation is strictly equal to a given orientation. 7 | // Strict equality requires identical basis and orientation values. 8 | // 9 | // Parameters: 10 | // orient 11 | // an Orientation 12 | // 13 | // Return 14 | // a boolean 15 | // 16 | 17 | if (orient && orient.basis && this.basis === orient.basis) { 18 | return orient2.equal(this.orient, orient.orient) 19 | } 20 | 21 | return false 22 | } 23 | -------------------------------------------------------------------------------- /lib/geometry/Orientation/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Orientation:getRaw() 3 | // 4 | // Return plain orient2 object `{a,b}` without basis data. 5 | // 6 | return this.orient 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Orientation/getUnitX.js: -------------------------------------------------------------------------------- 1 | module.exports = (Vector) => { 2 | return function () { 3 | // @Orientation.getUnitX() 4 | // 5 | // Get a unit vector along the x-axis of the orientation. 6 | // 7 | // Return 8 | // a Vector 9 | // 10 | return new Vector(this.basis, { 11 | x: this.orient.a, 12 | y: this.orient.b, 13 | z: 0 14 | }) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/geometry/Orientation/getUnitY.js: -------------------------------------------------------------------------------- 1 | module.exports = (Vector) => { 2 | return function () { 3 | // @Orientation.getUnitY() 4 | // 5 | // Get a unit vector along the y-axis of the orientation. 6 | // 7 | // Return 8 | // a Vector 9 | // 10 | return new Vector(this.basis, { 11 | x: -this.orient.b, 12 | y: this.orient.a, 13 | z: 0 14 | }) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/geometry/Orientation/getUnitZ.js: -------------------------------------------------------------------------------- 1 | module.exports = (Vector) => { 2 | return function () { 3 | // @Orientation.getUnitY() 4 | // 5 | // Get a unit vector along the z-axis of the orientation. 6 | // 7 | // Return 8 | // a Vector 9 | // 10 | return new Vector(this.basis, { 11 | x: 0, 12 | y: 0, 13 | z: 1 14 | }) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/geometry/Orientation/transitRaw.js: -------------------------------------------------------------------------------- 1 | const orient2 = require('affineplane').orient2 2 | 3 | module.exports = function (newBasis) { 4 | // @Orientation:transitRaw(newBasis) 5 | // 6 | // Represent the orientation in another basis. 7 | // Unlike changeBasis, returns a plain object without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // an orient2 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return orient2.transitFrom(this.orient, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Orientation/transitRawOuter.js: -------------------------------------------------------------------------------- 1 | const orient2 = require('affineplane').orient2 2 | 3 | module.exports = function (basis) { 4 | // @Orientation:transitRawOuter(basis) 5 | // 6 | // Represent the orientation in the outer basis of the given component. 7 | // Unlike changeBasis, returns a plain object without basis data. 8 | // See Point:transitRawOuter for details. 9 | // 10 | // Parameters: 11 | // basis 12 | // a Component 13 | // 14 | // Return 15 | // an orient2 16 | // 17 | const tran = this.basis.getTransitionToParentOf(basis) 18 | return orient2.transitFrom(this.orient, tran) 19 | } 20 | -------------------------------------------------------------------------------- /lib/geometry/Path/changeBasis.js: -------------------------------------------------------------------------------- 1 | const path3 = require('affineplane').path3 2 | 3 | module.exports = function (newBasis) { 4 | // @Path:changeBasis(newBasis) 5 | // 6 | // Transit the path to another coordinate basis. 7 | // 8 | // Parameters: 9 | // newBasis 10 | // a Component 11 | // 12 | // Return: 13 | // a Path 14 | // 15 | const tran = this.basis.getTransitionTo(newBasis) 16 | const npath = path3.transitFrom(this.path, tran) 17 | const Path = this.constructor 18 | return new Path(newBasis, npath) 19 | } 20 | -------------------------------------------------------------------------------- /lib/geometry/Path/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Path:getRaw() 3 | // 4 | // Return plain path3 object without basis data. 5 | // 6 | return this.path 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Path/transitRaw.js: -------------------------------------------------------------------------------- 1 | const path3 = require('affineplane').path3 2 | 3 | module.exports = function (newBasis) { 4 | // @Path:transitRaw(newBasis) 5 | // 6 | // Represent the path in another basis. 7 | // Unlike changeBasis, returns a plain object without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a path3, an array of point3 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return path3.transitFrom(this.path, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Point/addVector.js: -------------------------------------------------------------------------------- 1 | const point3 = require('affineplane').point3 2 | 3 | module.exports = function (vec) { 4 | // @Point:addVector(vec) 5 | // 6 | // Translate the point by a vector. 7 | // 8 | // Parameters: 9 | // vec 10 | // a Vector 11 | // 12 | // Return 13 | // a Point 14 | // 15 | 16 | // Rebase 17 | if (vec.transitRaw) { 18 | vec = vec.transitRaw(this.basis) 19 | } 20 | 21 | const po = point3.translate(this.point, vec) 22 | 23 | const Point = this.constructor 24 | return new Point(this.basis, po) 25 | } 26 | -------------------------------------------------------------------------------- /lib/geometry/Point/almostEqual.js: -------------------------------------------------------------------------------- 1 | const point3 = require('affineplane').point3 2 | 3 | module.exports = function (p, tolerance) { 4 | // @Point:almostEqual(p[, tolerance]) 5 | // 6 | // Test if the point is almost equal to p. 7 | // Almost equal requires strictly equal basis and 8 | // that coordinates are equal within tolerance. 9 | // 10 | // Parameters: 11 | // p 12 | // a Point 13 | // tolerance 14 | // optional number. Maximum Manhattan distance between this and p. 15 | // Default is affineplane.epsilon 16 | // 17 | // Return 18 | // a boolean 19 | // 20 | 21 | if (!p || !p.basis) return false 22 | if (this.basis !== p.basis) return false 23 | 24 | return point3.almostEqual(this.point, p.point, tolerance) 25 | } 26 | -------------------------------------------------------------------------------- /lib/geometry/Point/changeBasis.js: -------------------------------------------------------------------------------- 1 | const point3 = require('affineplane').point3 2 | 3 | module.exports = function (newBasis) { 4 | // @Point:changeBasis(newBasis) 5 | // 6 | // Parameters: 7 | // newBasis 8 | // a Component 9 | // 10 | // Return: 11 | // a Point 12 | // 13 | const tran = this.basis.getTransitionTo(newBasis) 14 | const xyz = point3.transitFrom(this.point, tran) 15 | const Point = this.constructor 16 | return new Point(newBasis, xyz) 17 | } 18 | -------------------------------------------------------------------------------- /lib/geometry/Point/equal.js: -------------------------------------------------------------------------------- 1 | const point3 = require('affineplane').point3 2 | 3 | module.exports = function (p) { 4 | // @Point:equal(p) 5 | // 6 | // Test if the point is strictly equal to p. 7 | // Strict equality requires strictly equal basis and coordinates. 8 | // 9 | // Parameters: 10 | // p 11 | // a Point 12 | // 13 | // Return 14 | // a boolean 15 | // 16 | 17 | if (p && p.basis && this.basis === p.basis) { 18 | return point3.equal(this.point, p.point) 19 | } 20 | 21 | return false 22 | } 23 | -------------------------------------------------------------------------------- /lib/geometry/Point/getDirectionTo.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const dir3 = fine.dir3 3 | const point3 = fine.point3 4 | 5 | module.exports = (Direction) => { 6 | // Use factory pattern to control circular dependency. 7 | return function (p) { 8 | // @Point:getDirectionTo(p) 9 | // 10 | // Direction from this point to another. 11 | // 12 | // Parameters: 13 | // p 14 | // a Point or {x,y,z}. The latter is assumed to be on the same basis. 15 | // 16 | // Return 17 | // a Distance 18 | // 19 | 20 | // Normalize to point3 21 | if (p.transitRaw) { 22 | p = p.transitRaw(this.basis) 23 | } 24 | 25 | const dir = dir3.fromVector(point3.diff(this.point, p)) 26 | 27 | return new Direction(this.basis, dir) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/geometry/Point/getDistanceTo.js: -------------------------------------------------------------------------------- 1 | const point3 = require('affineplane').point3 2 | 3 | module.exports = (Distance) => { 4 | // Use factory pattern to control circular dependency. 5 | return function (p) { 6 | // @Point:getDistanceTo(p) 7 | // 8 | // Distance between points. 9 | // 10 | // Parameters: 11 | // p 12 | // a Point or {x,y,z}. The latter is assumed to be on the same basis. 13 | // 14 | // Return 15 | // a Distance 16 | // 17 | 18 | // Normalize to point3 19 | if (p.transitRaw) { 20 | p = p.transitRaw(this.basis) 21 | } 22 | 23 | const dist = point3.distance(this.point, p) 24 | 25 | return new Distance(this.basis, dist) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/geometry/Point/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Point:getRaw() 3 | // 4 | // Return plain point3 object {x,y,z} without basis data. 5 | // 6 | return this.point 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Point/getVectorTo.js: -------------------------------------------------------------------------------- 1 | const point3 = require('affineplane').point3 2 | 3 | module.exports = (Vector) => { 4 | // Use factory pattern to control circular dependency. 5 | return function (p) { 6 | // @Point:getVectorTo(p) 7 | // 8 | // Get a vector from this to the point p. 9 | // 10 | // Parameters: 11 | // p 12 | // a Point 13 | // 14 | // Return: 15 | // a Vector on this basis. 16 | // 17 | 18 | // Normalize to point3 19 | if (p.transitRaw) { 20 | p = p.transitRaw(this.basis) 21 | } 22 | 23 | const dv = point3.diff(this.point, p) 24 | return new Vector(this.basis, dv) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/geometry/Point/projectTo.js: -------------------------------------------------------------------------------- 1 | const point3 = require('affineplane').point3 2 | 3 | module.exports = function (plane, camera) { 4 | // @Point:projectTo(plane, camera) 5 | // 6 | // Project the point onto a plane. 7 | // 8 | // Parameters: 9 | // plane 10 | // a Plane, the target plane. 11 | // camera 12 | // a Point, relative to the reference basis. 13 | // 14 | // Return 15 | // a Point, represented on the target basis. 16 | // 17 | 18 | // Normalize camera 19 | if (camera.transitRaw) { 20 | camera = camera.transitRaw(this.basis) 21 | } 22 | 23 | const pr = this.basis.getTransitionFrom(plane) 24 | 25 | const p = point3.projectTo(this.point, pr, camera) 26 | 27 | const Point = this.constructor 28 | return new Point(plane, p) 29 | } 30 | -------------------------------------------------------------------------------- /lib/geometry/Point/round.js: -------------------------------------------------------------------------------- 1 | const point3 = require('affineplane').point3 2 | 3 | module.exports = function () { 4 | // @Point:round() 5 | // 6 | // Round the point to nearest integers. 7 | // 8 | // Return 9 | // a Point 10 | // 11 | 12 | const rounded = point3.round(this.point) 13 | 14 | const Point = this.constructor 15 | return new Point(this.basis, rounded) 16 | } 17 | -------------------------------------------------------------------------------- /lib/geometry/Point/transitRaw.js: -------------------------------------------------------------------------------- 1 | const point3 = require('affineplane').point3 2 | 3 | module.exports = function (newBasis) { 4 | // @Point:transitRaw(newBasis) 5 | // 6 | // Represent the point in another basis. 7 | // Unlike changeBasis, returns a plain object without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a point3, an object. 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return point3.transitFrom(this.point, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Point/translateBy.js: -------------------------------------------------------------------------------- 1 | const point3 = require('affineplane').point3 2 | 3 | module.exports = function (vec) { 4 | // @Point:translateBy(vec) 5 | // 6 | // Get a point that has been translated i.e. offset by the given vector. 7 | // 8 | // Parameters: 9 | // vec 10 | // a Vector or vec3, the translation. 11 | // 12 | // Return 13 | // a Point 14 | // 15 | 16 | if (vec.transitRaw) { 17 | vec = vec.transitRaw(this.basis) 18 | } 19 | 20 | const p = point3.translate(this.point, vec) 21 | 22 | const Point = this.constructor 23 | return new Point(this.basis, p) 24 | } 25 | -------------------------------------------------------------------------------- /lib/geometry/Polygon/index.js: -------------------------------------------------------------------------------- 1 | // TODO 2 | -------------------------------------------------------------------------------- /lib/geometry/Ray/at.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const ray3 = fine.ray3 3 | const Point = require('../Point') 4 | 5 | module.exports = function (t) { 6 | // @Ray:at(t) 7 | // @Ray:getPoint 8 | // 9 | // Get a point along the ray. 10 | // The ray spanning vector defines the unit length. 11 | // Negative values default to the ray origin point. 12 | // 13 | // Parameters: 14 | // t 15 | // a number, the coordinate along the ray. 16 | // 17 | // Return: 18 | // a Point 19 | // 20 | 21 | const p = ray3.at(this.ray, t) 22 | return new Point(this.basis, p) 23 | } 24 | -------------------------------------------------------------------------------- /lib/geometry/Ray/create.js: -------------------------------------------------------------------------------- 1 | const ray3 = require('affineplane').ray3 2 | 3 | module.exports = (Ray) => { 4 | // 5 | return (basis, origin, span) => { 6 | // @Ray.create(basis, origin, span) 7 | // 8 | // Parameters: 9 | // basis 10 | // a Component, the basis for the ray 11 | // origin 12 | // a Point, the ray origin 13 | // span 14 | // a Vector, the ray direction 15 | // 16 | // Return 17 | // a Ray, in the given basis. 18 | // 19 | 20 | // Normalize 21 | if (origin.transitRaw) { 22 | origin = origin.transitRaw(basis) 23 | } 24 | if (span.transitRaw) { 25 | span = span.transitRaw(basis) 26 | } 27 | 28 | const ray = ray3.create(origin, span) 29 | 30 | return new Ray(basis, ray) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/geometry/Ray/getDistanceToPoint.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const point3 = fine.point3 3 | const Distance = require('../Distance') 4 | 5 | module.exports = function (point) { 6 | // @Ray:getDistanceToPoint(point) 7 | // 8 | // Compute shortest distance between the ray and the given point. 9 | // 10 | // Parameters: 11 | // point 12 | // a Point 13 | // 14 | // Return: 15 | // a Point 16 | // 17 | 18 | if (point.transitRaw) { 19 | point = point.transitRaw(this.basis) 20 | } 21 | 22 | const dist = point3.distanceToRay(point, this.ray) 23 | return new Distance(this.basis, dist) 24 | } 25 | -------------------------------------------------------------------------------- /lib/geometry/Ray/index.js: -------------------------------------------------------------------------------- 1 | const Ray = function (basis, ray) { 2 | // @Ray(basis, ray) 3 | // 4 | // A ray tensor. The ray is like a line but extends to infinity only 5 | // at one direction. 6 | // 7 | // Parameters 8 | // basis 9 | // a Component 10 | // ray 11 | // a ray3 object `{x,y,z,dx,dy,dz}` 12 | // 13 | this.basis = basis 14 | this.ray = ray 15 | } 16 | 17 | module.exports = Ray 18 | const proto = Ray.prototype 19 | proto.isRay = true 20 | 21 | // Class functions 22 | Ray.create = require('./create')(Ray) 23 | 24 | // Methods 25 | proto.at = require('./at') 26 | proto.getDistanceToPoint = require('./getDistanceToPoint') 27 | proto.getPoint = proto.at 28 | -------------------------------------------------------------------------------- /lib/geometry/Scale/changeBasis.js: -------------------------------------------------------------------------------- 1 | const dist3 = require('affineplane').dist3 2 | 3 | module.exports = function (newBasis) { 4 | // @Scale:changeBasis(newBasis) 5 | // 6 | // Represent the scale in another coordinate basis. 7 | // 8 | // Parameters: 9 | // newBasis 10 | // a Component 11 | // 12 | // Return 13 | // a Scale 14 | // 15 | const tr = this.basis.getTransitionTo(newBasis) 16 | const tscale = dist3.transitFrom(this.m, tr) 17 | const Scale = this.constructor 18 | return new Scale(newBasis, tscale) 19 | } 20 | -------------------------------------------------------------------------------- /lib/geometry/Scale/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Scale:getRaw() 3 | // 4 | // Return plain scale multiplier as a number without basis data. 5 | // 6 | return this.m 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Scale/scaleBy.js: -------------------------------------------------------------------------------- 1 | module.exports = function (multiplier) { 2 | // @Scale:scaleBy(multiplier) 3 | // 4 | // Multiply the scale. Returns new Scale. 5 | // 6 | // Parameters 7 | // multiplier 8 | // a number 9 | // 10 | // Return 11 | // a Scale 12 | // 13 | const Scale = this.constructor 14 | return new Scale(this.basis, this.m * multiplier) 15 | } 16 | -------------------------------------------------------------------------------- /lib/geometry/Scale/transitRaw.js: -------------------------------------------------------------------------------- 1 | const dist3 = require('affineplane').dist3 2 | 3 | module.exports = function (newBasis) { 4 | // @Scale:transitRaw(newBasis) 5 | // 6 | // Represent the scale in another basis. 7 | // Unlike changeBasis, returns a plain number without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a dist3, a number. 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return dist3.transitFrom(this.m, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Scale/transitRawOuter.js: -------------------------------------------------------------------------------- 1 | const dist3 = require('affineplane').dist3 2 | 3 | module.exports = function (basis) { 4 | // @Scale:transitRawOuter(basis) 5 | // 6 | // Represent the scale in the outer basis of the component. 7 | // Unlike changeBasis, returns a plain number without basis data. 8 | // See Point:transitRawOuter for details. 9 | // 10 | // Parameters: 11 | // basis 12 | // a Component 13 | // 14 | // Return 15 | // a number. 16 | // 17 | const tran = this.basis.getTransitionToParentOf(basis) 18 | return dist3.transitFrom(this.m, tran) 19 | } 20 | -------------------------------------------------------------------------------- /lib/geometry/Size/almostEqual.js: -------------------------------------------------------------------------------- 1 | const size3 = require('affineplane').size3 2 | 3 | module.exports = function (s, tolerance) { 4 | // @Size:almostEqual(s[, tolerance]) 5 | // 6 | // Test if this size is equal to the given size s within tolerance. 7 | // Equality requires strictly equal basis and that the difference in size 8 | // is within the tolerance. 9 | // 10 | // Parameters: 11 | // s 12 | // a Size 13 | // tolerance 14 | // optional number 15 | // 16 | // Return 17 | // a boolean 18 | // 19 | 20 | if (s && s.basis && this.basis === s.basis) { 21 | return size3.almostEqual(this.size, s.size, tolerance) 22 | } 23 | 24 | return false 25 | } 26 | -------------------------------------------------------------------------------- /lib/geometry/Size/changeBasis.js: -------------------------------------------------------------------------------- 1 | const size3 = require('affineplane').size3 2 | 3 | module.exports = function (newBasis) { 4 | // @Size:changeBasis(newBasis) 5 | // 6 | // The basis change of size does not preserve direction except 7 | // when the bases have same orientation. Rotation cannot be applied to size. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a Size 15 | // 16 | const pr = this.basis.getTransitionTo(newBasis) 17 | const ps = size3.transitFrom(this.size, pr) 18 | const Size = this.constructor 19 | return new Size(newBasis, ps) 20 | } 21 | -------------------------------------------------------------------------------- /lib/geometry/Size/equal.js: -------------------------------------------------------------------------------- 1 | const size3 = require('affineplane').size3 2 | 3 | module.exports = function (s) { 4 | // @Size:equal(s) 5 | // 6 | // Test if this size is strictly equal to the given size s. 7 | // Strict equality requires identical basis and size properties. 8 | // Additional properties are allowed. 9 | // 10 | // Parameters: 11 | // s 12 | // a Size 13 | // 14 | // Return 15 | // a boolean 16 | // 17 | 18 | if (s && s.basis && this.basis === s.basis) { 19 | return size3.equal(this.size, s.size) 20 | } 21 | 22 | return false 23 | } 24 | -------------------------------------------------------------------------------- /lib/geometry/Size/getArea.js: -------------------------------------------------------------------------------- 1 | const Area = require('../Area') 2 | 3 | module.exports = function () { 4 | // @Size:getArea() 5 | // 6 | // Get the area of the size. 7 | // 8 | // Return 9 | // an Area 10 | // 11 | 12 | const area = this.size.w * this.size.h 13 | 14 | return new Area(this.basis, area) 15 | } 16 | -------------------------------------------------------------------------------- /lib/geometry/Size/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Size:getRaw() 3 | // 4 | // Return plain size3 object `{ w, h, d }` without basis data. 5 | // 6 | return this.size 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Size/scaleBy.js: -------------------------------------------------------------------------------- 1 | const size3 = require('affineplane').size3 2 | 3 | module.exports = function (multiplier) { 4 | // @Size:scaleBy(multiplier) 5 | // 6 | // Multiply the size. 7 | // 8 | // Parameters: 9 | // multiplier 10 | // a number 11 | // 12 | // Return 13 | // a Size 14 | // 15 | const ms = size3.scaleBy(this.size, multiplier) 16 | const Size = this.constructor 17 | return new Size(this.basis, ms) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Size/transitRaw.js: -------------------------------------------------------------------------------- 1 | const size3 = require('affineplane').size3 2 | 3 | module.exports = function (newBasis) { 4 | // @Size:transitRaw(newBasis) 5 | // 6 | // Represent the size in another basis. 7 | // Unlike changeBasis, returns a plain object without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a size3, an object. 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return size3.transitFrom(this.size, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Sphere/almostEqual.js: -------------------------------------------------------------------------------- 1 | const sphere3 = require('affineplane').sphere3 2 | const almostEqual = sphere3.almostEqual 3 | 4 | module.exports = function (s, tolerance) { 5 | // @Sphere:almostEqual(s[, tolerance]) 6 | // 7 | // Test if this sphere is equal to the given sphere s within tolerance. 8 | // Almost equality requires identical basis 9 | // and that radius and position of the spheres are equal within tolerance. 10 | // 11 | // Parameters: 12 | // s 13 | // a Sphere 14 | // tolerance 15 | // optional number. Maximum tolerated distance between values. 16 | // 17 | // Return 18 | // a boolean 19 | // 20 | if (!s || !s.basis) { 21 | return false 22 | } 23 | 24 | return this.basis === s.basis && almostEqual(this.sphere, s.sphere, tolerance) 25 | } 26 | -------------------------------------------------------------------------------- /lib/geometry/Sphere/atCenter.js: -------------------------------------------------------------------------------- 1 | const Point = require('../Point') 2 | 3 | module.exports = function () { 4 | // @Sphere:atCenter() 5 | // @Sphere:atMid() 6 | // 7 | // Get the center point of the sphere. 8 | // 9 | // Return 10 | // a Point 11 | // 12 | 13 | return new Point(this.basis, { 14 | x: this.sphere.x, 15 | y: this.sphere.y, 16 | z: this.sphere.z 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Sphere/changeBasis.js: -------------------------------------------------------------------------------- 1 | const sphere3 = require('affineplane').sphere3 2 | 3 | module.exports = function (newBasis) { 4 | // @Sphere:changeBasis(newBasis) 5 | // 6 | // Transit the sphere to another basis. In other words, represent the same 7 | // geometry in the coordinate system of the given basis. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a Sphere 15 | // 16 | const pr = this.basis.getTransitionTo(newBasis) 17 | const pb = sphere3.transitFrom(this.sphere, pr) 18 | const Sphere = this.constructor 19 | return new Sphere(newBasis, pb) 20 | } 21 | -------------------------------------------------------------------------------- /lib/geometry/Sphere/equal.js: -------------------------------------------------------------------------------- 1 | const sphere3 = require('affineplane').sphere3 2 | const equal = sphere3.equal 3 | 4 | module.exports = function (s) { 5 | // @Sphere:equal(s) 6 | // 7 | // Test if this sphere is strictly equal with the given sphere s. 8 | // Strict equality requires that the spheres are on the same basis 9 | // and have strictly equal radius and position. 10 | // 11 | // Parameters: 12 | // s 13 | // a Sphere 14 | // 15 | // Return 16 | // a boolean 17 | // 18 | if (!s || !s.basis) { 19 | return false 20 | } 21 | 22 | return this.basis === s.basis && equal(this.sphere, s.sphere) 23 | } 24 | -------------------------------------------------------------------------------- /lib/geometry/Sphere/getDiameter.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../Distance') 2 | 3 | module.exports = function () { 4 | // @Sphere:getDiameter() 5 | // @Sphere:getDepth 6 | // @Sphere:getHeight 7 | // @Sphere:getWidth 8 | // 9 | // Get sphere diameter as a distance. The diameter equals the sphere 10 | // width, height, and depth. 11 | // 12 | // Return 13 | // a Distance 14 | // 15 | 16 | return new Distance(this.basis, this.sphere.r * 2) 17 | } 18 | -------------------------------------------------------------------------------- /lib/geometry/Sphere/getRadius.js: -------------------------------------------------------------------------------- 1 | const Distance = require('../Distance') 2 | 3 | module.exports = function () { 4 | // @Sphere:getRadius() 5 | // 6 | // Get sphere radius as a distance. 7 | // 8 | // Return 9 | // a Distance 10 | // 11 | return new Distance(this.basis, this.sphere.r) 12 | } 13 | -------------------------------------------------------------------------------- /lib/geometry/Sphere/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Sphere:getRaw() 3 | // 4 | // Return plain sphere3 object `{ x, y, z, r }` without basis data. 5 | // 6 | return this.sphere 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Sphere/getSize.js: -------------------------------------------------------------------------------- 1 | const Size = require('../Size') 2 | 3 | module.exports = function () { 4 | // @Sphere:getSize() 5 | // 6 | // Get sphere dimensions. 7 | // 8 | // Return 9 | // a Size 10 | // 11 | const d = this.sphere.r * 2 12 | return new Size(this.basis, { w: d, h: d, d }) 13 | } 14 | -------------------------------------------------------------------------------- /lib/geometry/Sphere/getVolume.js: -------------------------------------------------------------------------------- 1 | const sphere3 = require('affineplane').sphere3 2 | const Volume = require('../Volume') 3 | 4 | module.exports = function () { 5 | // @Sphere:getVolume() 6 | // 7 | // Get the volume of the sphere. 8 | // 9 | // Return 10 | // a Volume 11 | // 12 | const vol = sphere3.volume(this.sphere) 13 | return new Volume(this.basis, vol) 14 | } 15 | -------------------------------------------------------------------------------- /lib/geometry/Sphere/transitRaw.js: -------------------------------------------------------------------------------- 1 | const sphere3 = require('affineplane').sphere3 2 | 3 | module.exports = function (newBasis) { 4 | // @Sphere:transitRaw(newBasis) 5 | // 6 | // Represent the sphere in another basis. 7 | // Unlike changeBasis, returns a plain object without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a sphere3, an object. 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return sphere3.transitFrom(this.sphere, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Sphere/translateBy.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const sphere3 = fine.sphere3 3 | 4 | module.exports = function (vec) { 5 | // @Sphere:translateBy(vec) 6 | // 7 | // Translate the sphere by a vector. 8 | // 9 | // Parameters: 10 | // vec 11 | // a Vector, the translation 12 | // 13 | // Return 14 | // a Sphere, the translated sphere 15 | // 16 | 17 | // Normalize 18 | if (vec.transitRaw) { 19 | vec = vec.transitRaw(this.basis) 20 | } 21 | 22 | // Allow 2D vector 23 | vec.z = vec.z || 0 24 | 25 | const sphere = sphere3.translate(this.sphere, vec) 26 | 27 | const Sphere = this.constructor 28 | return new Sphere(this.basis, sphere) 29 | } 30 | -------------------------------------------------------------------------------- /lib/geometry/Transform/changeBasis.js: -------------------------------------------------------------------------------- 1 | const helm3 = require('affineplane').helm3 2 | 3 | module.exports = function (newBasis) { 4 | // @Transform:changeBasis(newBasis) 5 | // 6 | // Represent the transform on another plane. 7 | // 8 | // Parameters: 9 | // newBasis 10 | // a Component 11 | // 12 | // Return 13 | // a Transform 14 | // 15 | const tran = this.basis.getTransitionTo(newBasis) 16 | const h = helm3.transitFrom(this.helm, tran) 17 | const Transform = this.constructor 18 | return new Transform(newBasis, h) 19 | } 20 | -------------------------------------------------------------------------------- /lib/geometry/Transform/estimate.js: -------------------------------------------------------------------------------- 1 | module.exports = (Transform) => { 2 | return (basis, params) => { 3 | throw new Error('not implemented yet') 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /lib/geometry/Transform/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Transform:getRaw() 3 | // 4 | // Return plain helm3 object `{a,b,x,y,z}` without basis data. 5 | // 6 | return this.helm 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Transform/getRotation.js: -------------------------------------------------------------------------------- 1 | const fine = require('affineplane') 2 | const helm3 = fine.helm3 3 | 4 | module.exports = function () { 5 | // @Transform:getRotation() 6 | // 7 | // Get the rotating component of the transform without translation 8 | // and scaling. 9 | // 10 | // Return 11 | // a Transform 12 | // 13 | 14 | // TODO use affineplane v3 helm3.getRotation 15 | const m = helm3.getScale(this.helm) 16 | 17 | let helm 18 | if (m === 0) { 19 | helm = helm3.ROT0 20 | } else { 21 | helm = { 22 | a: this.helm.a / m, 23 | b: this.helm.b / m, 24 | x: 0, 25 | y: 0, 26 | z: 0 27 | } 28 | } 29 | 30 | const Transform = this.constructor 31 | return new Transform(this.basis, helm) 32 | } 33 | -------------------------------------------------------------------------------- /lib/geometry/Transform/getTranslation.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Transform:getTranslation() 3 | // 4 | // Get the translation component of the transform without rotation 5 | // and scaling. 6 | // 7 | // Return 8 | // a Transform 9 | // 10 | const Transform = this.constructor 11 | return new Transform(this.basis, { 12 | a: 1, 13 | b: 0, 14 | x: this.helm.x, 15 | y: this.helm.y, 16 | z: this.helm.z 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Transform/getVector.js: -------------------------------------------------------------------------------- 1 | module.exports = (Vector) => { 2 | return function () { 3 | // @Transform:getVector() 4 | // 5 | // Get the translation component of the transform as a Vector. 6 | // 7 | // Return 8 | // a Vector 9 | // 10 | return new Vector(this.basis, { 11 | x: this.helm.x, 12 | y: this.helm.y, 13 | z: this.helm.z 14 | }) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/geometry/Transform/inverse.js: -------------------------------------------------------------------------------- 1 | const helm3 = require('affineplane').helm3 2 | 3 | module.exports = function () { 4 | // @Transform:invert() 5 | // @Transform:inverse 6 | // 7 | // Invert the transform. 8 | // 9 | // Return 10 | // a Transform 11 | // 12 | const Transform = this.constructor 13 | return new Transform(this.basis, helm3.inverse(this.helm)) 14 | } 15 | -------------------------------------------------------------------------------- /lib/geometry/Transform/transitRaw.js: -------------------------------------------------------------------------------- 1 | const helm3 = require('affineplane').helm3 2 | 3 | module.exports = function (newBasis) { 4 | // @Transform:transitRaw(newBasis) 5 | // 6 | // Represent the transform in another basis. 7 | // Unlike changeBasis, returns a plain object without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a helm3, an object. 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return helm3.transitFrom(this.helm, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Vector/add.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = function (vec) { 4 | // @Vector:add(vec) 5 | // 6 | // Vector addition, this plus the given vector. 7 | // 8 | // Parameters: 9 | // vec 10 | // a Vector, or {x,y,z} in the same space. 11 | // 12 | // Return 13 | // a Vector 14 | // 15 | 16 | // Normalise to vec3 17 | if (vec.transitRaw) { 18 | vec = vec.transitRaw(this.basis) 19 | } 20 | 21 | const sum = vec3.add(this.vec, vec) 22 | 23 | const Vector = this.constructor 24 | return new Vector(this.basis, sum) 25 | } 26 | -------------------------------------------------------------------------------- /lib/geometry/Vector/almostEqual.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = function (vec, tolerance) { 4 | // @Vector:almostEqual(vec[, tolerance]) 5 | // 6 | // Test if vectors are almost equal. 7 | // 8 | // Parameters: 9 | // vec 10 | // a Vector, or {x,y,z} in the same space. 11 | // tolerance 12 | // optional number. Defaults to affineplane.epsilon 13 | // 14 | // Return 15 | // a boolean 16 | // 17 | 18 | // Normalise to vec3. 19 | if (vec.transitRaw) { 20 | vec = vec.transitRaw(this.basis) 21 | } 22 | 23 | // TODO normalize Distance tolerance? 24 | 25 | return vec3.almostEqual(this.vec, vec, tolerance) 26 | } 27 | -------------------------------------------------------------------------------- /lib/geometry/Vector/changeBasis.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = function (newBasis) { 4 | // @Vector:changeBasis(newBasis) 5 | // 6 | // Represent the vector in another basis. 7 | // Note that vectors are only affected by 8 | // scale and angle differences between bases. 9 | // 10 | // Parameters: 11 | // newBasis 12 | // a Component 13 | // 14 | // Return 15 | // a Vector 16 | // 17 | const tran = this.basis.getTransitionTo(newBasis) 18 | const vec = vec3.transitFrom(this.vec, tran) 19 | 20 | const Vector = this.constructor 21 | return new Vector(newBasis, vec) 22 | } 23 | -------------------------------------------------------------------------------- /lib/geometry/Vector/copy.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = function () { 4 | // @Vector:copy() 5 | // 6 | // Clone the vector. 7 | // 8 | // Return 9 | // a Vector, the clone. 10 | // 11 | const v = vec3.copy(this.vec) 12 | 13 | const Vector = this.constructor 14 | return new Vector(this.basis, v) 15 | } 16 | -------------------------------------------------------------------------------- /lib/geometry/Vector/cross.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = function (vec) { 4 | // @Vector:cross(vec) 5 | // 6 | // Get cross product with another vector. 7 | // 8 | // Parameters: 9 | // vec 10 | // a Vector, or {x,y,z} in the same space. 11 | // 12 | // Return 13 | // a Vector 14 | // 15 | 16 | // Normalise to vec3. 17 | if (vec.transitRaw) { 18 | vec = vec.transitRaw(this.basis) 19 | } 20 | 21 | const cv = vec3.cross(this.vec, vec) 22 | 23 | const Vector = this.constructor 24 | return new Vector(this.basis, cv) 25 | } 26 | -------------------------------------------------------------------------------- /lib/geometry/Vector/difference.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = function (vec) { 4 | // @Vector:difference(vec) 5 | // @Vector:subtract 6 | // 7 | // Vector subtraction, this minus the given vector. 8 | // 9 | // Parameters: 10 | // vec 11 | // a Vector, or {x,y,z} on the same basis. 12 | // 13 | // Return 14 | // a Vector 15 | // 16 | 17 | // Normalise vec to the same coordinate system. 18 | if (vec.transitRaw) { 19 | vec = vec.transitRaw(this.basis) 20 | } 21 | 22 | const delta = vec3.diff(this.vec, vec) 23 | 24 | const Vector = this.constructor 25 | return new Vector(this.basis, delta) 26 | } 27 | -------------------------------------------------------------------------------- /lib/geometry/Vector/dot.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = (Distance) => { 4 | return function (vec) { 5 | // @Vector:dot(vec) 6 | // 7 | // Get dot product with another vector. 8 | // 9 | // Parameters: 10 | // vec 11 | // a Vector, or {x,y,z} in the same space. 12 | // 13 | // Return 14 | // a Distance 15 | // 16 | 17 | // Normalise to vec3. 18 | if (vec.transitRaw) { 19 | vec = vec.transitRaw(this.basis) 20 | } 21 | 22 | const vd = vec3.dot(this.vec, vec) 23 | 24 | return new Distance(this.basis, vd) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/geometry/Vector/equal.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = function (v) { 4 | // @Vector:equal(v) 5 | // 6 | // Test if the vector is strictly equal to v. 7 | // Strict equality requires strictly equal basis and numerical components. 8 | // 9 | // Parameters: 10 | // v 11 | // a Vector 12 | // 13 | // Return 14 | // a boolean 15 | // 16 | 17 | if (v && v.basis && this.basis === v.basis) { 18 | return vec3.equal(this.vec, v.vec) 19 | } 20 | 21 | return false 22 | } 23 | -------------------------------------------------------------------------------- /lib/geometry/Vector/getDirection.js: -------------------------------------------------------------------------------- 1 | module.exports = (Direction) => { 2 | // Use factory pattern to control circular dependency. 3 | return function () { 4 | // @Vector:getDirection() 5 | // 6 | // Get vector direction. 7 | // 8 | // Return 9 | // a Direction 10 | // 11 | return Direction.createFromVector(this.basis, this.vec) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/geometry/Vector/getDistance.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = (Distance) => { 4 | // Use factory pattern to control circular dependency. 5 | return function () { 6 | // @Vector:getDistance() 7 | // @Vector:norm 8 | // 9 | // Get vector norm as a Distance. 10 | // 11 | // Return 12 | // a Distance 13 | // 14 | const magn = vec3.norm(this.vec) 15 | 16 | return new Distance(this.basis, magn) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Vector/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Vector:getRaw() 3 | // 4 | // Return plain vec3 object {x,y,z} without basis data. 5 | // 6 | return this.vec 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Vector/negate.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = function () { 4 | // @Vector:negate() 5 | // 6 | // Get same vector but in opposite direction. 7 | // 8 | // Return 9 | // a Vector 10 | // 11 | 12 | const iv = vec3.negate(this.vec) 13 | 14 | const Vector = this.constructor 15 | return new Vector(this.basis, iv) 16 | } 17 | -------------------------------------------------------------------------------- /lib/geometry/Vector/rotateBy.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = function (roll, pitch) { 4 | // @Vector:rotateBy(roll[, pitch]) 5 | // 6 | // Rotate the vector. The vector magnitude stays the same. 7 | // 8 | // Parameters: 9 | // roll 10 | // a number, roll angle in radians. 11 | // .. Right-hand rotation around z-axis. 12 | // pitch 13 | // optional number, pitch angle in radians. Default 0. 14 | // .. Right-hand rotation around x-axis. 15 | // 16 | // 17 | // Return 18 | // a Vector 19 | // 20 | const v = vec3.rotateBy(this.vec, roll, pitch) 21 | 22 | const Vector = this.constructor 23 | return new Vector(this.basis, v) 24 | } 25 | -------------------------------------------------------------------------------- /lib/geometry/Vector/scaleBy.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = function (multiplier) { 4 | // @Vector:scaleBy(multiplier) 5 | // 6 | // Scale the vector uniformly with multiplier. 7 | // 8 | // Parameters: 9 | // multiplier 10 | // a number 11 | // 12 | // Return 13 | // a Vector 14 | // 15 | 16 | const v = vec3.scaleBy(this.vec, multiplier) 17 | 18 | const Vector = this.constructor 19 | return new Vector(this.basis, v) 20 | } 21 | -------------------------------------------------------------------------------- /lib/geometry/Vector/transformBy.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = function (tr) { 4 | // @Vector:transformBy(tr) 5 | // 6 | // Transform the displacement vector by affine transformation. 7 | // This can rotate and scale the vector but cannot translate it 8 | // because only position vectors can be translated. 9 | // 10 | // Parameters: 11 | // tr 12 | // a Transform 13 | // 14 | // Return 15 | // a Vector 16 | // 17 | 18 | // Normalise transform to helm3 19 | if (tr.transitRaw) { 20 | tr = tr.transitRaw(this.basis) 21 | } 22 | 23 | const v = vec3.transformBy(this.vec, tr) 24 | 25 | const Vector = this.constructor 26 | return new Vector(this.basis, v) 27 | } 28 | -------------------------------------------------------------------------------- /lib/geometry/Vector/transitRaw.js: -------------------------------------------------------------------------------- 1 | const vec3 = require('affineplane').vec3 2 | 3 | module.exports = function (newBasis) { 4 | // @Vector:transitRaw(newBasis) 5 | // 6 | // Represent the vector in another basis. 7 | // Unlike changeBasis, returns a plain object without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a vec3, an object. 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return vec3.transitFrom(this.vec, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/geometry/Volume/changeBasis.js: -------------------------------------------------------------------------------- 1 | const scalar3 = require('affineplane').scalar3 2 | 3 | module.exports = function (newBasis) { 4 | // @Volume:changeBasis(newBasis) 5 | // 6 | // The basis change of a volume changes the basis and represents 7 | // the same volume in the target basis. 8 | // Only change in scale affects the representation. 9 | // 10 | // Parameters: 11 | // newBasis 12 | // a Component 13 | // 14 | // Return 15 | // a Volume 16 | // 17 | const pr = this.basis.getTransitionTo(newBasis) 18 | const pv = scalar3.transitFrom(this.volume, pr) 19 | const Volume = this.constructor 20 | return new Volume(newBasis, pv) 21 | } 22 | -------------------------------------------------------------------------------- /lib/geometry/Volume/getRaw.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @Volume:getRaw() 3 | // 4 | // Return plain volume as a number without basis data. 5 | // 6 | return this.volume 7 | } 8 | -------------------------------------------------------------------------------- /lib/geometry/Volume/transitRaw.js: -------------------------------------------------------------------------------- 1 | const scalar3 = require('affineplane').scalar3 2 | 3 | module.exports = function (newBasis) { 4 | // @Volume:transitRaw(newBasis) 5 | // 6 | // Represent the volume in another basis. 7 | // Unlike changeBasis, returns a plain number without basis data. 8 | // 9 | // Parameters: 10 | // newBasis 11 | // a Component 12 | // 13 | // Return 14 | // a scalar3, a number. The volume represented in the new basis. 15 | // 16 | const tran = this.basis.getTransitionTo(newBasis) 17 | return scalar3.transitFrom(this.volume, tran) 18 | } 19 | -------------------------------------------------------------------------------- /lib/interaction/KeyboardZoom/applyTransform.js: -------------------------------------------------------------------------------- 1 | module.exports = function (direction) { 2 | // @KeyboardZoom:applyTransform(direction) 3 | // 4 | // Parameters: 5 | // direction 6 | // a number, +1 or -1 7 | // 8 | 9 | if (this.source.isViewport) { 10 | // Normalize direction so that positive dir always means zooming in. 11 | const dir = (this.step >= 1 ? -Math.sign(direction) : Math.sign(direction)) 12 | 13 | const factor = Math.pow(this.step, dir) 14 | this.target.scaleBy(factor, this.source.atAnchor()) 15 | 16 | this.source.emit('keyzoom') 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/interaction/Pinch/getFreedom.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @tapspace.interaction.Pinch:getFreedom() 3 | // 4 | // Return 5 | // object, the freedom object 6 | // 7 | return this.options.freedom 8 | } 9 | -------------------------------------------------------------------------------- /lib/interaction/Pinch/hasAnyFreedom.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @tapspace.interaction.Pinch:hasAnyFreedom() 3 | // 4 | // Test if the interaction has any freedom dimensions enabled. 5 | // No freedoms is equivalent to disabled interaction. 6 | // 7 | // Return 8 | // a boolean 9 | // 10 | return this.options.freedom.type !== 'I' 11 | } 12 | -------------------------------------------------------------------------------- /lib/interaction/Resize/index.js: -------------------------------------------------------------------------------- 1 | // Resize element when touched on edges 2 | // 3 | -------------------------------------------------------------------------------- /lib/interaction/Tap/unbind.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @tapspace.interaction.Tap:unbind() 3 | // 4 | // Unbind capturer. 5 | // 6 | if (this.bound) { 7 | this.bound = false 8 | this.capturer.off('gesturstart', this.ongesturstart) 9 | this.capturer.off('gestureend', this.ongestureend) 10 | this.capturer.off('gesturecancel', this.ongesturecancel) 11 | this.capturer = null 12 | this.ongesturestart = null 13 | this.ongestureend = null 14 | this.ongesturecancel = null 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/interaction/Tap/update.js: -------------------------------------------------------------------------------- 1 | module.exports = function (newOptions) { 2 | // @tapspace.interaction.Tap:update(newOptions) 3 | // 4 | // Parameters: 5 | // newOptions 6 | // an object, with optional properties. See GestureCapturer:update 7 | // 8 | if (this.capturer) { 9 | this.capturer.update(newOptions) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/interaction/WheelPan/index.js: -------------------------------------------------------------------------------- 1 | // Pan the viewport using the mouse wheel 2 | // 3 | // TODO 4 | -------------------------------------------------------------------------------- /lib/interaction/WheelZoom/onwheel.js: -------------------------------------------------------------------------------- 1 | module.exports = (viewport) => { 2 | // Return: function, the wheel event handler. 3 | // 4 | return (ev) => { 5 | // The scaling factor. 6 | const factor = Math.pow(0.5, ev.deltaY / 500) 7 | // The scaling pivot stays fixed. 8 | // Use gesture center. Represented on the viewport. 9 | const pivot = ev.center 10 | 11 | // Just scale. 2D. 12 | viewport.scaleBy(1 / factor, pivot) 13 | 14 | // TODO limit extreme travel 15 | 16 | viewport.emit('wheel', ev) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/loaders/TreeLoader/closeAll.js: -------------------------------------------------------------------------------- 1 | module.exports = function (data) { 2 | // @TreeLoader:closeAll([data]) 3 | // 4 | // Close all spaces. Useful to clear loader before TreeLoader:initSpace call. 5 | // Makes the loader emit 'close' for each closing space. 6 | // The event should be handled at least by calling loader.removeSpace() 7 | // 8 | // Parameters: 9 | // data 10 | // optional object, the context data passed to 'close' event. 11 | // 12 | 13 | // Default data 14 | if (!data) { 15 | data = {} 16 | } 17 | 18 | Object.keys(this.spaces).forEach((id) => { 19 | const space = this.spaces[id] 20 | this.emit('close', { id, space, data }) 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /lib/loaders/TreeLoader/closeChildren.js: -------------------------------------------------------------------------------- 1 | module.exports = function (parentId, data) { 2 | // @TreeLoader:closeChildren(parentId[, data]) 3 | // 4 | // Close child spaces, given that the given space exists. 5 | // Synchronous. 6 | // 7 | // Parameters: 8 | // parentId 9 | // a string 10 | // data 11 | // optional object passed to 'close' event 12 | // 13 | 14 | const parentSpace = this.spaces[parentId] 15 | if (!parentSpace) { 16 | return 17 | } 18 | 19 | if (!data) { 20 | data = {} 21 | } 22 | 23 | // Find children. 24 | const kidIds = this.tracker(parentId, parentSpace) 25 | 26 | kidIds.forEach(kid => { 27 | this.closeChild(parentId, kid, data) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /lib/loaders/TreeLoader/countSpaces.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @TreeLoader:countSpaces() 3 | // 4 | // Get number of open spaces. 5 | // 6 | // Return: 7 | // a number 8 | // 9 | 10 | return Object.keys(this.spaces).length 11 | } 12 | -------------------------------------------------------------------------------- /lib/loaders/TreeLoader/getSpace.js: -------------------------------------------------------------------------------- 1 | module.exports = function (id) { 2 | // @TreeLoader:getSpace(id) 3 | // 4 | // Get a space by its ID. Null if does not exist. 5 | // 6 | // Parameters: 7 | // id 8 | // a string, the space ID 9 | // 10 | // Return 11 | // a Component or null 12 | // 13 | const s = this.spaces[id] 14 | if (s) { 15 | return s 16 | } 17 | return null 18 | } 19 | -------------------------------------------------------------------------------- /lib/loaders/TreeLoader/getSpaces.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // @TreeLoader:getSpaces() 3 | // 4 | // Get all loaded spaces. 5 | // 6 | // Return 7 | // an array of Component 8 | // 9 | return Object.values(this.spaces) 10 | } 11 | -------------------------------------------------------------------------------- /lib/loaders/TreeLoader/hasSpace.js: -------------------------------------------------------------------------------- 1 | module.exports = function (id) { 2 | // @TreeLoader:hasSpace(id) 3 | // 4 | // Test if the loader has the space by its ID. 5 | // Useful for checking if the space is ready or removed. 6 | // 7 | // Parameters: 8 | // id 9 | // a string, the space ID 10 | // 11 | // Return 12 | // a boolean 13 | // 14 | return id in this.spaces 15 | } 16 | -------------------------------------------------------------------------------- /lib/loaders/TreeLoader/openChildren.js: -------------------------------------------------------------------------------- 1 | module.exports = function (parentId, data) { 2 | // @TreeLoader:openChildren(parentId[, data]) 3 | // 4 | // Open all child spaces for the given parent id. 5 | // 6 | // Parameters: 7 | // parentId 8 | // a string, the parent space ID. 9 | // data 10 | // optional object, the context data passed to 'open' event. 11 | // 12 | 13 | // Parent may be removed. 14 | const parentSpace = this.spaces[parentId] 15 | if (!parentSpace) { 16 | return 17 | } 18 | 19 | if (!data) { 20 | data = {} 21 | } 22 | 23 | const childIds = this.tracker(parentId, parentSpace) 24 | 25 | for (let i = 0; i < childIds.length; i += 1) { 26 | const cid = childIds[i] 27 | this.openChild(parentId, cid, data) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/loaders/TreeLoader/removeSpace.js: -------------------------------------------------------------------------------- 1 | module.exports = function (spaceId) { 2 | // @TreeLoader:removeSpace(spaceId) 3 | // 4 | // Remove the space, given that the space exists. 5 | // Does not remove the children. 6 | // 7 | // Parameters: 8 | // parentId 9 | // a string 10 | // 11 | 12 | // The space may be loading. Ensure it is not loading anymore. 13 | delete this.loading[spaceId] 14 | 15 | // Space might be already removed. 16 | const space = this.spaces[spaceId] 17 | if (!space) { 18 | return 19 | } 20 | 21 | delete this.bases[spaceId] 22 | delete this.spaces[spaceId] 23 | 24 | // Remove from DOM 25 | space.remove() 26 | 27 | // Enable hooking to removals 28 | this.emit('closed', { id: spaceId }) 29 | } 30 | -------------------------------------------------------------------------------- /lib/loaders/index.js: -------------------------------------------------------------------------------- 1 | // @tapspace.loaders 2 | // 3 | // Helpers to preload content such as images to determine their dimensions 4 | // before inserting to space. Provides also utilities for recursive 5 | // loading, construction, and destruction. 6 | // 7 | 8 | exports.TreeLoader = require('./TreeLoader') 9 | exports.loadImages = require('./loadImages') 10 | -------------------------------------------------------------------------------- /lib/loaders/loadImages.js: -------------------------------------------------------------------------------- 1 | // @tapspace.loaders.loadImages(imgSrcs, callback) 2 | // 3 | // Preload one or more images and call back when finished. 4 | // 5 | // Usage: 6 | // ``` 7 | // tapspace.loaders.loadImages('hello.png', function (err, img) { 8 | // if (err) { throw err } 9 | // // img is now loaded and has correct dimensions instead of 0x0. 10 | // ... 11 | // }) 12 | // ``` 13 | // 14 | // See [loadimages](https://www.npmjs.com/package/loadimages) package 15 | // for details. 16 | // 17 | module.exports = require('loadimages') 18 | -------------------------------------------------------------------------------- /lib/metrics/index.js: -------------------------------------------------------------------------------- 1 | // @tapspace.metrics 2 | // 3 | // Tools to measure geometric properties of content in space. 4 | // 5 | exports.Measurement = require('./Measurement') 6 | -------------------------------------------------------------------------------- /lib/version.js: -------------------------------------------------------------------------------- 1 | // Generated by genversion. 2 | module.exports = '2.0.0-alpha.24' 3 | -------------------------------------------------------------------------------- /test/components/Arc/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | test('Arc:getLength', import.meta.dirname, 'getLength.html') 3 | } 4 | -------------------------------------------------------------------------------- /test/components/Component/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | test('Component: findCommonAncestor', import.meta.dirname, 'findCommonAncestor.html') 3 | test('Component: geometry creation', import.meta.dirname, 'geometryCreation.html') 4 | test('Component: prependChild', import.meta.dirname, 'prependChild.html') 5 | test('Component: removeChild', import.meta.dirname, 'removeChild.html') 6 | test('Component: replaceChild', import.meta.dirname, 'replaceChild.html') 7 | test('Component: replaceParent', import.meta.dirname, 'replaceParent.html') 8 | test('Component: setId', import.meta.dirname, 'setId.html') 9 | test('Component: setParent', import.meta.dirname, 'setParent.html') 10 | } 11 | -------------------------------------------------------------------------------- /test/components/FrameComponent/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | test('FrameComponent: resizeTo', import.meta.dirname, 'resizeTo.html') 3 | test('FrameComponent: transformToFill', import.meta.dirname, 'transformToFill.html') 4 | test('FrameComponent: transformToFit', import.meta.dirname, 'transformToFit.html') 5 | } 6 | -------------------------------------------------------------------------------- /test/components/Item/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Item' 3 | const methods = [ 4 | 'at', 5 | 'boundaries', 6 | 'createVector', 7 | 'getDistanceTo', 8 | 'getVectorTo', 9 | 'matchBasis', 10 | 'rotateBy', 11 | 'setBasis', 12 | 'setOrientation', 13 | 'setScale', 14 | 'transformBy' 15 | ] 16 | 17 | let i, m 18 | for (i = 0; i < methods.length; i += 1) { 19 | m = methods[i] 20 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/components/Space/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | test('Space:at', import.meta.dirname, 'at.html') 3 | test('Space:getBoundingBox', import.meta.dirname, 'getBoundingBox.html') 4 | } 5 | -------------------------------------------------------------------------------- /test/components/Viewport/getAspectRatio.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Viewport:getAspectRatio – Tapspace Test 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/components/Viewport/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | test('Viewport:getAspectRatio', import.meta.dirname, 'getAspectRatio.html') 3 | test('Viewport:measureGroup', import.meta.dirname, 'measureGroup.html') 4 | } 5 | -------------------------------------------------------------------------------- /test/components/default.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | width: 100%; 5 | height: 100%; 6 | } 7 | 8 | #container { 9 | background: gray; 10 | width: 100%; 11 | height: 100%; 12 | } 13 | 14 | #testspace { 15 | width: 640px; 16 | height: 480px; 17 | } 18 | -------------------------------------------------------------------------------- /test/components/index.mjs: -------------------------------------------------------------------------------- 1 | import testArc from './Arc/index.mjs' 2 | import testComponent from './Component/index.mjs' 3 | import testFrameComponent from './FrameComponent/index.mjs' 4 | import testItem from './Item/index.mjs' 5 | import testSpace from './Space/index.mjs' 6 | import testViewport from './Viewport/index.mjs' 7 | import { makeTestAllOk } from '../utils.mjs' 8 | 9 | export default function (test, browser) { 10 | // Custom test runner to reduce boilerplate code. 11 | // Opens the test page and evaluates the test results. 12 | const testAllOk = makeTestAllOk(test, browser) 13 | 14 | testArc(testAllOk) 15 | testComponent(testAllOk) 16 | testFrameComponent(testAllOk) 17 | testItem(testAllOk) 18 | testSpace(testAllOk) 19 | testViewport(testAllOk) 20 | } 21 | -------------------------------------------------------------------------------- /test/geometry/Area/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Area' 3 | const methods = [ 4 | 'projectTo', 5 | 'transitRaw' 6 | ] 7 | 8 | let i, m 9 | for (i = 0; i < methods.length; i += 1) { 10 | m = methods[i] 11 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/geometry/Basis/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Basis' 3 | const methods = [ 4 | 'at', 5 | 'changeBasis', 6 | 'createDirection', 7 | 'createVector', 8 | 'getMatchedOuter', 9 | 'getTransformTo', 10 | 'offsets', 11 | 'rotateBy', 12 | 'rotateByDegrees', 13 | 'scaleBy', 14 | 'transformBy', 15 | 'transitRaw', 16 | 'transitRawOuter', 17 | 'translateBy' 18 | ] 19 | 20 | let i, m 21 | for (i = 0; i < methods.length; i += 1) { 22 | m = methods[i] 23 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/geometry/Box/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Box' 3 | const methods = [ 4 | 'almostEqual', 5 | 'at', 6 | 'atNorm', 7 | 'changeBasis', 8 | 'detectCollision', 9 | 'equal', 10 | 'fromBoxes', 11 | 'fromPoints', 12 | 'getArea', 13 | 'getBoundingBox', 14 | 'getBoundingCircle', 15 | 'getBoundingSphere', 16 | 'getInnerSquare', 17 | 'getVolume', 18 | 'normAt', 19 | 'projectTo', 20 | 'resizeTo', 21 | 'rotateBy', 22 | 'scaleBy', 23 | 'transitRaw', 24 | 'translateBy' 25 | ] 26 | 27 | let i, m 28 | for (i = 0; i < methods.length; i += 1) { 29 | m = methods[i] 30 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/geometry/Circle/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Circle' 3 | const methods = [ 4 | 'boundaries', 5 | 'collisions', 6 | 'construction', 7 | 'dimensions', 8 | 'equality', 9 | 'points', 10 | 'transformations', 11 | 'transitions', 12 | 'translations' 13 | ] 14 | 15 | let i, m 16 | for (i = 0; i < methods.length; i += 1) { 17 | m = methods[i] 18 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/geometry/Direction/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Direction' 3 | const methods = [ 4 | 'getRaw' 5 | ] 6 | 7 | let i, m 8 | for (i = 0; i < methods.length; i += 1) { 9 | m = methods[i] 10 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/geometry/Distance/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Distance' 3 | const methods = [ 4 | 'comparison', 5 | 'getRaw', 6 | 'projection' 7 | ] 8 | 9 | let i, m 10 | for (i = 0; i < methods.length; i += 1) { 11 | m = methods[i] 12 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/geometry/Orientation/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Orientation' 3 | const methods = [ 4 | 'equality', 5 | 'getRaw' 6 | ] 7 | 8 | let i, m 9 | for (i = 0; i < methods.length; i += 1) { 10 | m = methods[i] 11 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/geometry/Path/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Path' 3 | const methods = [ 4 | 'getRaw' 5 | ] 6 | 7 | let i, m 8 | for (i = 0; i < methods.length; i += 1) { 9 | m = methods[i] 10 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/geometry/Point/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Point' 3 | const methods = [ 4 | 'addVector', 5 | 'getDistanceTo', 6 | 'getRaw', 7 | 'transformBy', 8 | 'transitRawOuter' 9 | ] 10 | 11 | let i, m 12 | for (i = 0; i < methods.length; i += 1) { 13 | m = methods[i] 14 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/geometry/Ray/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Ray' 3 | const methods = [ 4 | 'creation' 5 | ] 6 | 7 | let i, m 8 | for (i = 0; i < methods.length; i += 1) { 9 | m = methods[i] 10 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/geometry/Scale/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Scale' 3 | const methods = [ 4 | 'changeBasis' 5 | ] 6 | 7 | let i, m 8 | for (i = 0; i < methods.length; i += 1) { 9 | m = methods[i] 10 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/geometry/Size/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Size' 3 | const methods = [ 4 | 'equality', 5 | 'getRaw', 6 | 'scaleBy' 7 | ] 8 | 9 | let i, m 10 | for (i = 0; i < methods.length; i += 1) { 11 | m = methods[i] 12 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/geometry/Sphere/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Sphere' 3 | const methods = [ 4 | 'boundaries', 5 | 'collisions', 6 | 'construction', 7 | 'dimensions', 8 | 'equality', 9 | 'measures', 10 | 'points', 11 | 'transformations', 12 | 'transitions', 13 | 'translations' 14 | ] 15 | 16 | let i, m 17 | for (i = 0; i < methods.length; i += 1) { 18 | m = methods[i] 19 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test/geometry/Transform/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Transform' 3 | const methods = [ 4 | 'getRaw' 5 | ] 6 | 7 | let i, m 8 | for (i = 0; i < methods.length; i += 1) { 9 | m = methods[i] 10 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/geometry/Vector/index.mjs: -------------------------------------------------------------------------------- 1 | export default function (test) { 2 | const namespace = 'Vector' 3 | const methods = [ 4 | 'fromPolar', 5 | 'fromSpherical', 6 | 'getRaw' 7 | ] 8 | 9 | let i, m 10 | for (i = 0; i < methods.length; i += 1) { 11 | m = methods[i] 12 | test(namespace + ':' + m, import.meta.dirname, m + '.html') 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/geometry/default.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | width: 100%; 5 | height: 100%; 6 | } 7 | 8 | #container { 9 | background: gray; 10 | width: 100%; 11 | height: 100%; 12 | } 13 | 14 | #testspace { 15 | width: 640px; 16 | height: 480px; 17 | } 18 | -------------------------------------------------------------------------------- /test/suites.mjs: -------------------------------------------------------------------------------- 1 | import testComponents from './components/index.mjs' 2 | import testGeometry from './geometry/index.mjs' 3 | import testVersion from './version/index.mjs' 4 | 5 | export default function (test, browser) { 6 | testComponents(test, browser) 7 | testGeometry(test, browser) 8 | testVersion(test, browser) 9 | } 10 | -------------------------------------------------------------------------------- /test/version/version.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | version – Tapspace Test 6 | 7 | 8 | 9 |
10 |
11 |
12 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const path = require('path') 3 | 4 | module.exports = { 5 | entry: './index', 6 | output: { 7 | filename: 'tapspace.min.js', 8 | path: path.join(__dirname, '/dist'), 9 | sourceMapFilename: '[file].map', 10 | library: 'tapspace', // module name in global scope 11 | libraryTarget: 'umd' 12 | }, 13 | 14 | mode: 'development' 15 | } 16 | --------------------------------------------------------------------------------